wow
This commit is contained in:
+4
@@ -0,0 +1,4 @@
|
||||
.DS_Store
|
||||
|
||||
node_modules
|
||||
npm-debug.log
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
language: javascript
|
||||
script: phantomjs spec/lib/run_jasmine_test.coffee spec/spec_runner.html
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
module.exports = (grunt) ->
|
||||
|
||||
grunt.initConfig
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
uglify:
|
||||
options:
|
||||
banner: '/*! <%= pkg.name %> v<%= pkg.version %> @licence MIT */'
|
||||
main:
|
||||
files:
|
||||
'src/mailcheck.min.js': 'src/mailcheck.js'
|
||||
|
||||
jasmine_node:
|
||||
specNameMatcher: "spec"
|
||||
requirejs: false
|
||||
forceExit: true
|
||||
jUnit:
|
||||
report: false
|
||||
savePath : "spec"
|
||||
useDotNotation: true
|
||||
consolidate: true
|
||||
|
||||
grunt.loadNpmTasks 'grunt-contrib-uglify'
|
||||
grunt.loadNpmTasks 'grunt-jasmine-node'
|
||||
|
||||
grunt.registerTask 'default', ['jasmine_node', 'uglify']
|
||||
grunt.registerTask 'test', ['jasmine_node']
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
Copyright © 2012 Received Inc, http://kicksend.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Software”), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
mailcheck.js
|
||||
=========
|
||||
|
||||
[](https://travis-ci.org/mailcheck/mailcheck)
|
||||
|
||||
The Javascript library and jQuery plugin that suggests a right domain when your users misspell it in an email address.
|
||||
|
||||
What does it do?
|
||||
----------------
|
||||
|
||||
When your user types in "user@gmil.con", Mailcheck will suggest "user@gmail.com".
|
||||
|
||||
Mailcheck will offer up suggestions for second and top level domains too. For example when a user types in "user@hotmail.cmo" then "hotmail.com" will be suggested. Similarly if only the second level domain is miss-spelled then it will be corrected independently of the top level domain allowing for fine resolution tweaking if desired.
|
||||
|
||||
At [Kicksend](http://kicksend.com), we use Mailcheck to help reduce typos in email addresses during sign ups. It has [reduced our sign up confirmation email bounces by 50%](http://blog.kicksend.com/how-we-decreased-sign-up-confirmation-email-b).
|
||||
|
||||

|
||||
|
||||
See it live in action [here](http://kicksend.com/join).
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
For instant use, download the minified library [mailcheck.min.js](https://raw.githubusercontent.com/mailcheck/mailcheck/d25dc9a119ca844bb35b1baf341cca0a634e4ac9/src/mailcheck.min.js) into your javascripts directory. [mailcheck.js](https://raw.githubusercontent.com/mailcheck/mailcheck/d25dc9a119ca844bb35b1baf341cca0a634e4ac9/src/mailcheck.js) is also available unminimised if you want to hack on it, or have your own minimizer.
|
||||
|
||||
#### Bower ####
|
||||
|
||||
```
|
||||
> bower install --save mailcheck
|
||||
```
|
||||
|
||||
#### Node/Browserify ####
|
||||
|
||||
```
|
||||
> npm install --save mailcheck
|
||||
```
|
||||
|
||||
Usage with jQuery
|
||||
-----
|
||||
|
||||
First, include jQuery and Mailcheck into the page.
|
||||
|
||||
```html
|
||||
<script src="jquery.min.js"></script>
|
||||
<script src="mailcheck.min.js"></script>
|
||||
```
|
||||
|
||||
Have a text field.
|
||||
|
||||
```html
|
||||
<input id="email" name="email" type="email" />
|
||||
```
|
||||
|
||||
Now, attach Mailcheck to the text field. You can declare an array of domains, second level domains and top level domains you want to check against.
|
||||
|
||||
```html
|
||||
<script>
|
||||
var domains = ['gmail.com', 'aol.com'];
|
||||
var secondLevelDomains = ['hotmail']
|
||||
var topLevelDomains = ["com", "net", "org"];
|
||||
|
||||
var superStringDistance = function(string1, string2) {
|
||||
// a string distance algorithm of your choosing
|
||||
}
|
||||
|
||||
$('#email').on('blur', function() {
|
||||
$(this).mailcheck({
|
||||
domains: domains, // optional
|
||||
secondLevelDomains: secondLevelDomains, // optional
|
||||
topLevelDomains: topLevelDomains, // optional
|
||||
distanceFunction: superStringDistance, // optional
|
||||
suggested: function(element, suggestion) {
|
||||
// callback code
|
||||
},
|
||||
empty: function(element) {
|
||||
// callback code
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
Mailcheck takes in two callbacks, `suggested` and `empty`. We recommend you supply both.
|
||||
|
||||
`suggested` is called when there's a suggestion. Mailcheck passes in the target element and the suggestion. The suggestion is an object with the following members:
|
||||
|
||||
```js
|
||||
{
|
||||
address: 'test', // the address; part before the @ sign
|
||||
domain: 'gmail.com', // the suggested domain
|
||||
full: '[email protected]' // the full suggested email
|
||||
}
|
||||
```
|
||||
Mailcheck does not want to get in the way of how you can show suggestions. Use the suggestion object to display suggestions in your preferred manner.
|
||||
|
||||
`empty` is called when there's no suggestion. Mailcheck just passes in the target element. It is a good idea to use this callback to clear an existing suggestion.
|
||||
|
||||
Usage without jQuery
|
||||
--------------------
|
||||
|
||||
Mailcheck is decoupled from jQuery, so its usage without jQuery is almost identical.
|
||||
|
||||
Using the example from above, you would call `Mailcheck.run` instead.
|
||||
|
||||
```html
|
||||
<script>
|
||||
Mailcheck.run({
|
||||
email: yourTextInput.value,
|
||||
domains: domains, // optional
|
||||
topLevelDomains: topLevelDomains, // optional
|
||||
secondLevelDomains: secondLevelDomains, // optional
|
||||
distanceFunction: superStringDistance, // optional
|
||||
suggested: function(suggestion) {
|
||||
// callback code
|
||||
},
|
||||
empty: function() {
|
||||
// callback code
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
The rest works similarly. In fact, the Mailcheck jQuery plugin just wraps `Mailcheck.run`.
|
||||
|
||||
Usage on Node.js
|
||||
----------------
|
||||
|
||||
If you're running this on Node.js, you can just `require('mailcheck')` to get the `mailcheck` object, and call `run` on that:
|
||||
|
||||
```js
|
||||
var mailcheck = require('mailcheck');
|
||||
|
||||
mailcheck.run({
|
||||
// see 'usage without jQuery' above.
|
||||
});
|
||||
```
|
||||
|
||||
Domains
|
||||
-------
|
||||
|
||||
Mailcheck has inbuilt defaults if the `domains`, `secondLevelDomains` or `topLevelDomains` options aren't provided. We still recommend supplying your own domains based on the distribution of your users.
|
||||
|
||||
#### Adding your own Domains ####
|
||||
|
||||
You can replace Mailcheck's default domain/TLD suggestions by supplying replacements to `mailcheck.run`:
|
||||
|
||||
```js
|
||||
Mailcheck.run({
|
||||
domains: ['customdomain.com', 'anotherdomain.net'], // replaces existing domains
|
||||
secondLevelDomains: ['domain', 'yetanotherdomain'], // replaces existing SLDs
|
||||
topLevelDomains: ['com.au', 'ru'] // replaces existing TLDs
|
||||
});
|
||||
```
|
||||
|
||||
Alternatively, you can *extend* Mailcheck's global set of default domains & TLDs by adding items to `Mailcheck.defaultDomains` and `Mailcheck.defaultTopLevelDomains`:
|
||||
|
||||
```js
|
||||
Mailcheck.defaultDomains.push('customdomain.com', 'anotherdomain.net') // extend existing domains
|
||||
Mailcheck.defaultSecondLevelDomains.push('domain', 'yetanotherdomain') // extend existing SLDs
|
||||
Mailcheck.defaultTopLevelDomains.push('com.au', 'ru') // extend existing TLDs
|
||||
```
|
||||
|
||||
Customization
|
||||
-------------
|
||||
|
||||
The Mailcheck jQuery plugin wraps Mailcheck. The prime candidates for customization are the methods
|
||||
`Mailcheck.findClosestDomain` and `Mailcheck.stringDistance`.
|
||||
|
||||
Mailcheck currently uses the [sift3](http://siderite.blogspot.com/2007/04/super-fast-and-accurate-string-distance.html) string similarity algorithm by [Siderite](http://siderite.blogspot.com/). You can modify the inbuilt string distance function, or pass in your own when calling Mailcheck.
|
||||
|
||||
Since Mailcheck runs client side, keep in mind file size, memory usage and performance.
|
||||
|
||||
Tests
|
||||
-----
|
||||
|
||||
Mailcheck is tested with [Jasmine](https://jasmine.github.io/). Load `spec/spec_runner.html` in your browser to run the tests or run `npm test` from the command line to test in node.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Let's make Mailcheck awesome. We're on the lookout for maintainers and [contributors](https://github.com/mailcheck/mailcheck/contributors).
|
||||
|
||||
And do send in those pull requests! To get them accepted, please:
|
||||
|
||||
- Test your code. Add test cases to `spec/mailcheckSpec.js`, and run it across browsers (yes, including IE).
|
||||
- Minify the plugin by running `grunt` in the Mailcheck directory.
|
||||
|
||||
Upcoming features, bugs and feature requests are managed in [Issues](https://github.com/mailcheck/mailcheck/issues).
|
||||
|
||||
Developing on Nitrous.IO
|
||||
---------------------
|
||||
|
||||
Start contributing to Mailcheck instantly on [Nitrous.IO](https://www.nitrous.io/?utm_source=github.com&utm_campaign=kicksend-mailcheck&utm_medium=hackonnitrous):
|
||||
|
||||
[](https://www.nitrous.io/hack_button?source=embed&runtime=nodejs&repo=kicksend%2Fmailcheck&file_to_open=README.nitrous.md)
|
||||
|
||||
|
||||
Who have used Mailcheck?
|
||||
-----------------------
|
||||
|
||||
- [Kicksend](http://kicksend.com/)
|
||||
- [Dropbox](http://dropbox.com/)
|
||||
- [Kickstarter](http://kickstarter.com/)
|
||||
- [Uber](http://uber.com/)
|
||||
- [Khan Academy](http://khanacademy.org/)
|
||||
- [Minecraft](http://minecraft.net/)
|
||||
- [Paperless Post](http://www.paperlesspost.com/)
|
||||
- [The Verge](http://theverge.com/)
|
||||
- [SB Nation](http://sbnation.com/)
|
||||
- [Kippt](http://kippt.com/)
|
||||
- [Shopify](http://shopify.com/)
|
||||
|
||||
Do you use Mailcheck? [Tweet me](http://twitter.com/derrickko) your link.
|
||||
|
||||
Related Links
|
||||
-------------
|
||||
|
||||
- [Official Mailcheck Ports](https://github.com/mailcheck/)
|
||||
- [Two ways to reduce bounced welcome emails](http://blog.postmarkapp.com/post/19685472721/two-ways-to-reduce-bounced-welcome-emails) by [Postmark](http://postmark.com)
|
||||
- [MooTools port](https://github.com/DimitarChristoff/mailcheck) by [Dimitar Christoff](https://github.com/DimitarChristoff)
|
||||
- [WordPress Plugin](https://github.com/bmoredrew/mailcheck-wordpress-plugin/) by [Drew Poland](https://github.com/bmoredrew)
|
||||
- [WordPress e-Commerce Plugin](http://wordpress.org/extend/plugins/e-commerce-mailcheck/)
|
||||
- [Mailcheck for Drupal](http://drupal.org/project/Mailcheck) by [Martin Elvar](https://twitter.com/MartinElvar)
|
||||
|
||||
Core Team
|
||||
-------
|
||||
|
||||
- Derrick Ko, [Kicksend](http://kicksend.com), [@derrickko](http://twitter.com/derrickko). Created Mailcheck.
|
||||
- Wei Lu, [Hive](https://www.hivewallet.com/), [@luweidewei](http://twitter.com/luweidewei).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Released under the MIT License.
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
## Get started on Nitrous.IO
|
||||
|
||||
Run the following commands in the terminal below:
|
||||
|
||||
1. `cd ~/workspace/mailcheck`
|
||||
2. `npm install`
|
||||
3. `./script/server`
|
||||
|
||||
Go to the "Preview Menu" and click "Port 3000"
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "mailcheck",
|
||||
"main": "src/mailcheck.js",
|
||||
"scripts": [
|
||||
"src/mailcheck.js",
|
||||
"src/mailcheck.min.js"
|
||||
],
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"components"
|
||||
]
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
+38
@@ -0,0 +1,38 @@
|
||||
<html>
|
||||
<head>
|
||||
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="../src/mailcheck.js"></script>
|
||||
<title>Mailcheck</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Mailcheck</h1>
|
||||
<p>For a demo, enter [email protected] or [email protected] in the field below:</p>
|
||||
<input id="email" name="email" type="email" />
|
||||
<input name="Submit" type="submit" />
|
||||
<p id="suggestion"></p>
|
||||
|
||||
<script>
|
||||
var domains = ['hotmail.com', 'gmail.com', 'aol.com'];
|
||||
var topLevelDomains = ["com", "net", "org"];
|
||||
|
||||
$('#email').on('blur', function(event) {
|
||||
console.log("event ", event);
|
||||
console.log("this ", $(this));
|
||||
$(this).mailcheck({
|
||||
domains: domains, // optional
|
||||
topLevelDomains: topLevelDomains, // optional
|
||||
suggested: function(element, suggestion) {
|
||||
// callback code
|
||||
console.log("suggestion ", suggestion.full);
|
||||
$('#suggestion').html("Did you mean <b><i>" + suggestion.full + "</b></i>?");
|
||||
},
|
||||
empty: function(element) {
|
||||
// callback code
|
||||
$('#suggestion').html('No Suggestions :(');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "mailcheck",
|
||||
"title": "mailcheck",
|
||||
"description": "The Javascript library and jQuery plugin that suggests a right domain when your users misspell it in an email address.",
|
||||
"keywords": [
|
||||
"form",
|
||||
"email",
|
||||
"spell-check"
|
||||
],
|
||||
"version": "1.1.1",
|
||||
"author": {
|
||||
"name": "Derrick Ko",
|
||||
"url": "http://derrickko.com",
|
||||
"email": "[email protected]"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Wei Lu",
|
||||
"url": "http://neo.com",
|
||||
"email": "[email protected]"
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/mailcheck/mailcheck/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"bugs": "https://github.com/mailcheck/mailcheck/issues",
|
||||
"homepage": "https://github.com/mailcheck/mailcheck",
|
||||
"docs": "https://github.com/mailcheck/mailcheck/blob/master/README.md",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.7"
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "mailcheck",
|
||||
"title": "mailcheck",
|
||||
"description": "A standalone module that suggests a right domain when your users misspell it in an email address.",
|
||||
"keywords": [
|
||||
"form",
|
||||
"email",
|
||||
"spell check"
|
||||
],
|
||||
"version": "1.1.1",
|
||||
"author": {
|
||||
"name": "Derrick Ko",
|
||||
"url": "http://derrickko.com",
|
||||
"email": "[email protected]"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Wei Lu",
|
||||
"url": "http://neo.com",
|
||||
"email": "[email protected]"
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/mailcheck/mailcheck/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"bugs": "https://github.com/mailcheck/mailcheck/issues",
|
||||
"homepage": "https://github.com/mailcheck/mailcheck",
|
||||
"docs": "https://github.com/mailcheck/mailcheck/blob/master/README.md",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"jasmine-node": "1.x",
|
||||
"grunt": "0.4.x",
|
||||
"grunt-contrib-uglify": "~0.2.4",
|
||||
"grunt-jasmine-node": "~0.1.0",
|
||||
"grunt-cli": "~0.1.9"
|
||||
},
|
||||
"main": "src/mailcheck.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/mailcheck/mailcheck.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
"prepublish": "grunt test",
|
||||
"uglify": "grunt uglify"
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
ABSOLUTE_PATH=$(cd `dirname "${BASH_SOURCE[0]}"` && pwd)/../examples
|
||||
node script/server.js 3000 $ABSOLUTE_PATH
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
var http = require("http"),
|
||||
url = require("url"),
|
||||
path = require("path"),
|
||||
fs = require("fs"),
|
||||
base = process.argv[3] || process.cwd(),
|
||||
port = process.argv[2] || 8888;
|
||||
|
||||
http.createServer(function(request, response) {
|
||||
|
||||
var uri = url.parse(request.url).pathname
|
||||
, filename = path.join(base, uri);
|
||||
|
||||
fs.exists(filename, function(exists) {
|
||||
console.log("File Name ", filename);
|
||||
|
||||
if(!exists) {
|
||||
response.writeHead(404, {"Content-Type": "text/plain"});
|
||||
response.write("404 Not Found\n");
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
|
||||
|
||||
fs.readFile(filename, "binary", function(err, file) {
|
||||
if(err) {
|
||||
response.writeHead(500, {"Content-Type": "text/plain"});
|
||||
response.write(err + "\n");
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
response.writeHead(200);
|
||||
response.write(file, "binary");
|
||||
response.end();
|
||||
});
|
||||
});
|
||||
}).listen(parseInt(port, 10), '0.0.0.0');
|
||||
|
||||
console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown");
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
Jasmine Reporter that outputs test results to the browser console.
|
||||
Useful for running in a headless environment such as PhantomJs, ZombieJs etc.
|
||||
|
||||
Usage:
|
||||
// From your html file that loads jasmine:
|
||||
jasmine.getEnv().addReporter(new jasmine.ConsoleReporter());
|
||||
jasmine.getEnv().execute();
|
||||
*/
|
||||
|
||||
(function(jasmine, console) {
|
||||
if (!jasmine) {
|
||||
throw "jasmine library isn't loaded!";
|
||||
}
|
||||
|
||||
var ANSI = {}
|
||||
ANSI.color_map = {
|
||||
"green" : 32,
|
||||
"red" : 31
|
||||
}
|
||||
|
||||
ANSI.colorize_text = function(text, color) {
|
||||
var color_code = this.color_map[color];
|
||||
return "\033[" + color_code + "m" + text + "\033[0m";
|
||||
}
|
||||
|
||||
var ConsoleReporter = function() {
|
||||
if (!console || !console.log) { throw "console isn't present!"; }
|
||||
this.status = this.statuses.stopped;
|
||||
};
|
||||
|
||||
var proto = ConsoleReporter.prototype;
|
||||
proto.statuses = {
|
||||
stopped : "stopped",
|
||||
running : "running",
|
||||
fail : "fail",
|
||||
success : "success"
|
||||
};
|
||||
|
||||
proto.reportRunnerStarting = function(runner) {
|
||||
this.status = this.statuses.running;
|
||||
this.start_time = (new Date()).getTime();
|
||||
this.executed_specs = 0;
|
||||
this.passed_specs = 0;
|
||||
this.log("Starting...");
|
||||
};
|
||||
|
||||
proto.reportRunnerResults = function(runner) {
|
||||
var failed = this.executed_specs - this.passed_specs;
|
||||
var spec_str = this.executed_specs + (this.executed_specs === 1 ? " spec, " : " specs, ");
|
||||
var fail_str = failed + (failed === 1 ? " failure in " : " failures in ");
|
||||
var color = (failed > 0)? "red" : "green";
|
||||
var dur = (new Date()).getTime() - this.start_time;
|
||||
|
||||
this.log("");
|
||||
this.log("Finished");
|
||||
this.log("-----------------");
|
||||
this.log(spec_str + fail_str + (dur/1000) + "s.", color);
|
||||
|
||||
this.status = (failed > 0)? this.statuses.fail : this.statuses.success;
|
||||
|
||||
/* Print something that signals that testing is over so that headless browsers
|
||||
like PhantomJs know when to terminate. */
|
||||
this.log("");
|
||||
this.log("ConsoleReporter finished");
|
||||
};
|
||||
|
||||
|
||||
proto.reportSpecStarting = function(spec) {
|
||||
this.executed_specs++;
|
||||
};
|
||||
|
||||
proto.reportSpecResults = function(spec) {
|
||||
if (spec.results().passed()) {
|
||||
this.passed_specs++;
|
||||
return;
|
||||
}
|
||||
|
||||
var resultText = spec.suite.description + " : " + spec.description;
|
||||
this.log(resultText, "red");
|
||||
|
||||
var items = spec.results().getItems()
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var trace = items[i].trace.stack || items[i].trace;
|
||||
this.log(trace, "red");
|
||||
}
|
||||
};
|
||||
|
||||
proto.reportSuiteResults = function(suite) {
|
||||
if (!suite.parentSuite) { return; }
|
||||
var results = suite.results();
|
||||
var failed = results.totalCount - results.passedCount;
|
||||
var color = (failed > 0)? "red" : "green";
|
||||
this.log(suite.description + ": " + results.passedCount + " of " + results.totalCount + " passed.", color);
|
||||
};
|
||||
|
||||
proto.log = function(str, color) {
|
||||
var text = (color != undefined)? ANSI.colorize_text(str, color) : str;
|
||||
console.log(text)
|
||||
};
|
||||
|
||||
jasmine.ConsoleReporter = ConsoleReporter;
|
||||
})(jasmine, console);
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2008-2011 Pivotal Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+616
@@ -0,0 +1,616 @@
|
||||
jasmine.HtmlReporterHelpers = {};
|
||||
|
||||
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
||||
var results = child.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
||||
var parentDiv = this.dom.summary;
|
||||
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
||||
var parent = child[parentSuite];
|
||||
|
||||
if (parent) {
|
||||
if (typeof this.views.suites[parent.id] == 'undefined') {
|
||||
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
||||
}
|
||||
parentDiv = this.views.suites[parent.id].element;
|
||||
}
|
||||
|
||||
parentDiv.appendChild(childElement);
|
||||
};
|
||||
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
||||
for(var fn in jasmine.HtmlReporterHelpers) {
|
||||
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter = function(_doc) {
|
||||
var self = this;
|
||||
var doc = _doc || window.document;
|
||||
|
||||
var reporterView;
|
||||
|
||||
var dom = {};
|
||||
|
||||
// Jasmine Reporter Public Interface
|
||||
self.logRunningSpecs = false;
|
||||
|
||||
self.reportRunnerStarting = function(runner) {
|
||||
var specs = runner.specs() || [];
|
||||
|
||||
if (specs.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
createReporterDom(runner.env.versionString());
|
||||
doc.body.appendChild(dom.reporter);
|
||||
|
||||
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
|
||||
reporterView.addSpecs(specs, self.specFilter);
|
||||
};
|
||||
|
||||
self.reportRunnerResults = function(runner) {
|
||||
reporterView && reporterView.complete();
|
||||
};
|
||||
|
||||
self.reportSuiteResults = function(suite) {
|
||||
reporterView.suiteComplete(suite);
|
||||
};
|
||||
|
||||
self.reportSpecStarting = function(spec) {
|
||||
if (self.logRunningSpecs) {
|
||||
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
self.reportSpecResults = function(spec) {
|
||||
reporterView.specComplete(spec);
|
||||
};
|
||||
|
||||
self.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.specFilter = function(spec) {
|
||||
if (!focusedSpecName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return spec.getFullName().indexOf(focusedSpecName()) === 0;
|
||||
};
|
||||
|
||||
return self;
|
||||
|
||||
function focusedSpecName() {
|
||||
var specName;
|
||||
|
||||
(function memoizeFocusedSpec() {
|
||||
if (specName) {
|
||||
return;
|
||||
}
|
||||
|
||||
var paramMap = [];
|
||||
var params = doc.location.search.substring(1).split('&');
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
specName = paramMap.spec;
|
||||
})();
|
||||
|
||||
return specName;
|
||||
}
|
||||
|
||||
function createReporterDom(version) {
|
||||
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
|
||||
dom.banner = self.createDom('div', { className: 'banner' },
|
||||
self.createDom('span', { className: 'title' }, "Jasmine "),
|
||||
self.createDom('span', { className: 'version' }, version)),
|
||||
|
||||
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
|
||||
dom.alert = self.createDom('div', {className: 'alert'}),
|
||||
dom.results = self.createDom('div', {className: 'results'},
|
||||
dom.summary = self.createDom('div', { className: 'summary' }),
|
||||
dom.details = self.createDom('div', { id: 'details' }))
|
||||
);
|
||||
}
|
||||
};
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {
|
||||
this.startedAt = new Date();
|
||||
this.runningSpecCount = 0;
|
||||
this.completeSpecCount = 0;
|
||||
this.passedCount = 0;
|
||||
this.failedCount = 0;
|
||||
this.skippedCount = 0;
|
||||
|
||||
this.createResultsMenu = function() {
|
||||
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
|
||||
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
|
||||
' | ',
|
||||
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
|
||||
|
||||
this.summaryMenuItem.onclick = function() {
|
||||
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
|
||||
};
|
||||
|
||||
this.detailsMenuItem.onclick = function() {
|
||||
showDetails();
|
||||
};
|
||||
};
|
||||
|
||||
this.addSpecs = function(specs, specFilter) {
|
||||
this.totalSpecCount = specs.length;
|
||||
|
||||
this.views = {
|
||||
specs: {},
|
||||
suites: {}
|
||||
};
|
||||
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
var spec = specs[i];
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
|
||||
if (specFilter(spec)) {
|
||||
this.runningSpecCount++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.specComplete = function(spec) {
|
||||
this.completeSpecCount++;
|
||||
|
||||
if (isUndefined(this.views.specs[spec.id])) {
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
|
||||
}
|
||||
|
||||
var specView = this.views.specs[spec.id];
|
||||
|
||||
switch (specView.status()) {
|
||||
case 'passed':
|
||||
this.passedCount++;
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.failedCount++;
|
||||
break;
|
||||
|
||||
case 'skipped':
|
||||
this.skippedCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
specView.refresh();
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
this.suiteComplete = function(suite) {
|
||||
var suiteView = this.views.suites[suite.id];
|
||||
if (isUndefined(suiteView)) {
|
||||
return;
|
||||
}
|
||||
suiteView.refresh();
|
||||
};
|
||||
|
||||
this.refresh = function() {
|
||||
|
||||
if (isUndefined(this.resultsMenu)) {
|
||||
this.createResultsMenu();
|
||||
}
|
||||
|
||||
// currently running UI
|
||||
if (isUndefined(this.runningAlert)) {
|
||||
this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
|
||||
dom.alert.appendChild(this.runningAlert);
|
||||
}
|
||||
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
|
||||
|
||||
// skipped specs UI
|
||||
if (isUndefined(this.skippedAlert)) {
|
||||
this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
|
||||
}
|
||||
|
||||
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.skippedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.skippedAlert);
|
||||
}
|
||||
|
||||
// passing specs UI
|
||||
if (isUndefined(this.passedAlert)) {
|
||||
this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
|
||||
}
|
||||
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
|
||||
|
||||
// failing specs UI
|
||||
if (isUndefined(this.failedAlert)) {
|
||||
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
|
||||
}
|
||||
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
|
||||
|
||||
if (this.failedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.failedAlert);
|
||||
dom.alert.appendChild(this.resultsMenu);
|
||||
}
|
||||
|
||||
// summary info
|
||||
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
|
||||
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
|
||||
};
|
||||
|
||||
this.complete = function() {
|
||||
dom.alert.removeChild(this.runningAlert);
|
||||
|
||||
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.failedCount === 0) {
|
||||
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
|
||||
} else {
|
||||
showDetails();
|
||||
}
|
||||
|
||||
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function showDetails() {
|
||||
if (dom.reporter.className.search(/showDetails/) === -1) {
|
||||
dom.reporter.className += " showDetails";
|
||||
}
|
||||
}
|
||||
|
||||
function isUndefined(obj) {
|
||||
return typeof obj === 'undefined';
|
||||
}
|
||||
|
||||
function isDefined(obj) {
|
||||
return !isUndefined(obj);
|
||||
}
|
||||
|
||||
function specPluralizedFor(count) {
|
||||
var str = count + " spec";
|
||||
if (count > 1) {
|
||||
str += "s"
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
|
||||
|
||||
|
||||
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
|
||||
this.spec = spec;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.symbol = this.createDom('li', { className: 'pending' });
|
||||
this.dom.symbolSummary.appendChild(this.symbol);
|
||||
|
||||
this.summary = this.createDom('div', { className: 'specSummary' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.description)
|
||||
);
|
||||
|
||||
this.detail = this.createDom('div', { className: 'specDetail' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.getFullName())
|
||||
);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.spec);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
|
||||
this.symbol.className = this.status();
|
||||
|
||||
switch (this.status()) {
|
||||
case 'skipped':
|
||||
break;
|
||||
|
||||
case 'passed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
this.appendFailureDetail();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
|
||||
this.summary.className += ' ' + this.status();
|
||||
this.appendToSummary(this.spec, this.summary);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
|
||||
this.detail.className += ' ' + this.status();
|
||||
|
||||
var resultItems = this.spec.results().getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
this.detail.appendChild(messagesDiv);
|
||||
this.dom.details.appendChild(this.detail);
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
|
||||
this.suite = suite;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.element = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
|
||||
);
|
||||
|
||||
this.appendToSummary(this.suite, this.element);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.suite);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
|
||||
this.element.className += " " + this.status();
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
|
||||
|
||||
/* @deprecated Use jasmine.HtmlReporter instead
|
||||
*/
|
||||
jasmine.TrivialReporter = function(doc) {
|
||||
this.document = doc || document;
|
||||
this.suiteDivs = {};
|
||||
this.logRunningSpecs = false;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) { el.appendChild(child); }
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
|
||||
var showPassed, showSkipped;
|
||||
|
||||
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
|
||||
this.createDom('div', { className: 'banner' },
|
||||
this.createDom('div', { className: 'logo' },
|
||||
this.createDom('span', { className: 'title' }, "Jasmine"),
|
||||
this.createDom('span', { className: 'version' }, runner.env.versionString())),
|
||||
this.createDom('div', { className: 'options' },
|
||||
"Show ",
|
||||
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
|
||||
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
|
||||
)
|
||||
),
|
||||
|
||||
this.runnerDiv = this.createDom('div', { className: 'runner running' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
|
||||
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
|
||||
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
|
||||
);
|
||||
|
||||
this.document.body.appendChild(this.outerDiv);
|
||||
|
||||
var suites = runner.suites();
|
||||
for (var i = 0; i < suites.length; i++) {
|
||||
var suite = suites[i];
|
||||
var suiteDiv = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
|
||||
this.suiteDivs[suite.id] = suiteDiv;
|
||||
var parentDiv = this.outerDiv;
|
||||
if (suite.parentSuite) {
|
||||
parentDiv = this.suiteDivs[suite.parentSuite.id];
|
||||
}
|
||||
parentDiv.appendChild(suiteDiv);
|
||||
}
|
||||
|
||||
this.startedAt = new Date();
|
||||
|
||||
var self = this;
|
||||
showPassed.onclick = function(evt) {
|
||||
if (showPassed.checked) {
|
||||
self.outerDiv.className += ' show-passed';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
|
||||
}
|
||||
};
|
||||
|
||||
showSkipped.onclick = function(evt) {
|
||||
if (showSkipped.checked) {
|
||||
self.outerDiv.className += ' show-skipped';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
|
||||
var results = runner.results();
|
||||
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
|
||||
this.runnerDiv.setAttribute("class", className);
|
||||
//do it twice for IE
|
||||
this.runnerDiv.setAttribute("className", className);
|
||||
var specs = runner.specs();
|
||||
var specCount = 0;
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
if (this.specFilter(specs[i])) {
|
||||
specCount++;
|
||||
}
|
||||
}
|
||||
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
|
||||
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
|
||||
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
|
||||
|
||||
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
|
||||
var results = suite.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.totalCount === 0) { // todo: change this to check results.skipped
|
||||
status = 'skipped';
|
||||
}
|
||||
this.suiteDivs[suite.id].className += " " + status;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
|
||||
if (this.logRunningSpecs) {
|
||||
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
|
||||
var results = spec.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
var specDiv = this.createDom('div', { className: 'spec ' + status },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(spec.getFullName()),
|
||||
title: spec.getFullName()
|
||||
}, spec.description));
|
||||
|
||||
|
||||
var resultItems = results.getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
specDiv.appendChild(messagesDiv);
|
||||
}
|
||||
|
||||
this.suiteDivs[spec.suite.id].appendChild(specDiv);
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.getLocation = function() {
|
||||
return this.document.location;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
|
||||
var paramMap = {};
|
||||
var params = this.getLocation().search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
if (!paramMap.spec) {
|
||||
return true;
|
||||
}
|
||||
return spec.getFullName().indexOf(paramMap.spec) === 0;
|
||||
};
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
|
||||
|
||||
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
|
||||
#HTMLReporter a { text-decoration: none; }
|
||||
#HTMLReporter a:hover { text-decoration: underline; }
|
||||
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
|
||||
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
|
||||
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#HTMLReporter .version { color: #aaaaaa; }
|
||||
#HTMLReporter .banner { margin-top: 14px; }
|
||||
#HTMLReporter .duration { color: #aaaaaa; float: right; }
|
||||
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
|
||||
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
|
||||
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
|
||||
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
|
||||
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
|
||||
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
|
||||
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
|
||||
#HTMLReporter .runningAlert { background-color: #666666; }
|
||||
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
|
||||
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
|
||||
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
|
||||
#HTMLReporter .passingAlert { background-color: #a6b779; }
|
||||
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
|
||||
#HTMLReporter .failingAlert { background-color: #cf867e; }
|
||||
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
|
||||
#HTMLReporter .results { margin-top: 14px; }
|
||||
#HTMLReporter #details { display: none; }
|
||||
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .summary { display: none; }
|
||||
#HTMLReporter.showDetails #details { display: block; }
|
||||
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter .summary { margin-top: 14px; }
|
||||
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
|
||||
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
|
||||
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
|
||||
#HTMLReporter .description + .suite { margin-top: 0; }
|
||||
#HTMLReporter .suite { margin-top: 14px; }
|
||||
#HTMLReporter .suite a { color: #333333; }
|
||||
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
|
||||
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
|
||||
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
|
||||
#HTMLReporter .resultMessage span.result { display: block; }
|
||||
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
|
||||
|
||||
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
|
||||
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
|
||||
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
|
||||
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
|
||||
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
|
||||
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
|
||||
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
|
||||
#TrivialReporter .runner.running { background-color: yellow; }
|
||||
#TrivialReporter .options { text-align: right; font-size: .8em; }
|
||||
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
|
||||
#TrivialReporter .suite .suite { margin: 5px; }
|
||||
#TrivialReporter .suite.passed { background-color: #dfd; }
|
||||
#TrivialReporter .suite.failed { background-color: #fdd; }
|
||||
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
|
||||
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
|
||||
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
|
||||
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
|
||||
#TrivialReporter .spec.skipped { background-color: #bbb; }
|
||||
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
|
||||
#TrivialReporter .passed { background-color: #cfc; display: none; }
|
||||
#TrivialReporter .failed { background-color: #fbb; }
|
||||
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
|
||||
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
|
||||
#TrivialReporter .resultMessage .mismatch { color: black; }
|
||||
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
|
||||
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
|
||||
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
|
||||
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
|
||||
+2529
File diff suppressed because it is too large
Load Diff
+9266
File diff suppressed because it is too large
Load Diff
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/local/bin/phantomjs
|
||||
|
||||
# Runs a Jasmine Suite from an html page
|
||||
# @page is a PhantomJs page object
|
||||
# @exit_func is the function to call in order to exit the script
|
||||
|
||||
class PhantomJasmineRunner
|
||||
constructor: (@page, @exit_func = phantom.exit) ->
|
||||
@tries = 0
|
||||
@max_tries = 10
|
||||
|
||||
get_status: -> @page.evaluate(-> console_reporter.status)
|
||||
|
||||
terminate: ->
|
||||
switch @get_status()
|
||||
when "success" then @exit_func 0
|
||||
when "fail" then @exit_func 1
|
||||
else @exit_func 2
|
||||
|
||||
# Script Begin
|
||||
if phantom.args.length == 0
|
||||
console.log "Need a url as the argument"
|
||||
phantom.exit 1
|
||||
|
||||
page = new WebPage()
|
||||
|
||||
runner = new PhantomJasmineRunner(page)
|
||||
|
||||
# Don't supress console output
|
||||
page.onConsoleMessage = (msg) ->
|
||||
console.log msg
|
||||
|
||||
# Terminate when the reporter singals that testing is over.
|
||||
# We cannot use a callback function for this (because page.evaluate is sandboxed),
|
||||
# so we have to *observe* the website.
|
||||
if msg == "ConsoleReporter finished"
|
||||
runner.terminate()
|
||||
|
||||
address = phantom.args[0]
|
||||
|
||||
page.open address, (status) ->
|
||||
if status != "success"
|
||||
console.log "can't load the address!"
|
||||
phantom.exit 1
|
||||
|
||||
# Now we wait until onConsoleMessage reads the termination signal from the log.
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
describe("mailcheck", function() {
|
||||
var domains = ['google.com', 'gmail.com', 'emaildomain.com', 'comcast.net', 'facebook.com', 'msn.com', 'gmx.de'];
|
||||
var secondLevelDomains = ["yahoo", "hotmail", "mail", "live", "outlook", "gmx"];
|
||||
var topLevelDomains = ['co.uk', 'com', 'org', 'info', 'fr'];
|
||||
|
||||
describe("Mailcheck", function(){
|
||||
var mailcheck;
|
||||
|
||||
beforeEach(function(){
|
||||
// We may be running in a CommonJS environment.
|
||||
// If so, mailcheck won't be in a global Kicksend object.
|
||||
mailcheck = typeof Mailcheck !== 'undefined' ? Mailcheck : require('../');
|
||||
});
|
||||
|
||||
describe("run", function () {
|
||||
var suggestedSpy, emptySpy;
|
||||
|
||||
beforeEach(function () {
|
||||
suggestedSpy = jasmine.createSpy();
|
||||
emptySpy = jasmine.createSpy();
|
||||
});
|
||||
|
||||
it("calls the 'suggested' callback with the element and result when there's a suggestion", function () {
|
||||
mailcheck.run({
|
||||
email: '[email protected]',
|
||||
suggested:suggestedSpy,
|
||||
empty:emptySpy
|
||||
});
|
||||
|
||||
expect(suggestedSpy).toHaveBeenCalledWith({
|
||||
address:'test',
|
||||
domain:'gmail.com',
|
||||
full:'[email protected]'
|
||||
});
|
||||
|
||||
expect(emptySpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls the 'empty' callback with the element when there's no suggestion", function () {
|
||||
mailcheck.run({
|
||||
email: '[email protected]',
|
||||
suggested:suggestedSpy,
|
||||
empty:emptySpy
|
||||
});
|
||||
|
||||
expect(suggestedSpy).not.toHaveBeenCalled();
|
||||
|
||||
expect(emptySpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns the result when 'suggested' callback is not defined", function () {
|
||||
var result = mailcheck.run({
|
||||
email: '[email protected]'
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
address:'test',
|
||||
domain:'gmail.com',
|
||||
full:'[email protected]'
|
||||
})
|
||||
})
|
||||
|
||||
it("takes in an array of specified domains", function () {
|
||||
mailcheck.run({
|
||||
email: '[email protected]',
|
||||
suggested:suggestedSpy,
|
||||
empty:emptySpy,
|
||||
domains:domains
|
||||
});
|
||||
|
||||
expect(suggestedSpy).toHaveBeenCalledWith({
|
||||
address:'test',
|
||||
domain:'emaildomain.com',
|
||||
full:'[email protected]'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("encodeEmail", function () {
|
||||
it("escapes the element's value", function () {
|
||||
var result = mailcheck.encodeEmail('<script>alert("a")</script>@emaildomain.con');
|
||||
expect(result).not.toMatch(/<script>/);
|
||||
});
|
||||
|
||||
it("allows valid special characters", function() {
|
||||
var result = mailcheck.encodeEmail( " g1!#$%&'*+-/=?^_`{|}@gmai.com")
|
||||
expect(result).toEqual(" g1!#$%&'*+-/=?^_`{|}@gmai.com");
|
||||
});
|
||||
});
|
||||
|
||||
describe("return value", function () {
|
||||
it("is a hash representing the email address", function () {
|
||||
var result = mailcheck.suggest('[email protected]', domains);
|
||||
|
||||
expect(result).toEqual({
|
||||
address: 'test',
|
||||
domain: 'gmail.com',
|
||||
full: '[email protected]'
|
||||
});
|
||||
});
|
||||
|
||||
it("is false when no suggestion is found", function() {
|
||||
expect(mailcheck.suggest('[email protected]', domains)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("is false when an incomplete email is provided", function(){
|
||||
expect(mailcheck.suggest('contact', domains)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("cases", function () {
|
||||
it("pass", function () {
|
||||
expect(mailcheck.suggest('[email protected]', domains).domain).toEqual('emaildomain.com');
|
||||
expect(mailcheck.suggest('[email protected]', domains).domain).toEqual('gmail.com');
|
||||
expect(mailcheck.suggest('[email protected]', domains).domain).toEqual('gmail.com');
|
||||
expect(mailcheck.suggest('[email protected]', domains).domain).toEqual('gmail.com');
|
||||
expect(mailcheck.suggest('test@#gmail.com', domains).domain).toEqual('gmail.com');
|
||||
expect(mailcheck.suggest('[email protected]', domains).domain).toEqual('comcast.net');
|
||||
|
||||
expect(mailcheck.suggest('[email protected]', domains, secondLevelDomains, topLevelDomains).domain).toEqual('hotmail.com');
|
||||
expect(mailcheck.suggest('[email protected]', domains, secondLevelDomains, topLevelDomains).domain).toEqual('hotmail.com');
|
||||
expect(mailcheck.suggest('[email protected]', domains, secondLevelDomains, topLevelDomains).domain).toEqual('yahoo.com');
|
||||
expect(mailcheck.suggest('[email protected]', domains, secondLevelDomains, topLevelDomains).domain).toEqual('randomsmallcompany.com');
|
||||
|
||||
expect(mailcheck.suggest('', domains)).toBeFalsy();
|
||||
expect(mailcheck.suggest('test@', domains)).toBeFalsy();
|
||||
expect(mailcheck.suggest('test', domains)).toBeFalsy();
|
||||
|
||||
/* This test is for illustrative purposes as the splitEmail function should return a better
|
||||
* representation of the true top-level domain in the case of an email address with subdomains.
|
||||
* mailcheck will be unable to return a suggestion in the case of this email address.
|
||||
*/
|
||||
expect(mailcheck.suggest('[email protected]', domains, secondLevelDomains, topLevelDomains).domain).toBeFalsy();
|
||||
});
|
||||
|
||||
it("will not offer a suggestion that itself leads to another suggestion", function() {
|
||||
var suggestion = mailcheck.suggest('[email protected]', domains, secondLevelDomains, topLevelDomains);
|
||||
expect(suggestion.domain).toEqual('yahoo.com');
|
||||
});
|
||||
|
||||
it("will not offer suggestions for valid 2ld-tld combinations", function() {
|
||||
expect(
|
||||
mailcheck.suggest('[email protected]', domains, secondLevelDomains, topLevelDomains)
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it("will not offer suggestions for valid 2ld-tld even if theres a close fully-specified domain", function() {
|
||||
expect(
|
||||
mailcheck.suggest('[email protected]', domains, secondLevelDomains, topLevelDomains)
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("mailcheck.splitEmail", function () {
|
||||
it("returns a hash of the address, the domain, and the top-level domain", function () {
|
||||
expect(mailcheck.splitEmail('[email protected]')).toEqual({
|
||||
address:'test',
|
||||
domain:'example.com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: 'example'
|
||||
});
|
||||
|
||||
expect(mailcheck.splitEmail('[email protected]')).toEqual({
|
||||
address:'test',
|
||||
domain:'example.co.uk',
|
||||
topLevelDomain:'co.uk',
|
||||
secondLevelDomain: 'example'
|
||||
});
|
||||
|
||||
/* This test is for illustrative purposes as the splitEmail function should return a better
|
||||
* representation of the true top-level domain in the case of an email address with subdomains.
|
||||
*/
|
||||
expect(mailcheck.splitEmail('[email protected]')).toEqual({
|
||||
address:'test',
|
||||
domain:'mail.randomsmallcompany.co.uk',
|
||||
topLevelDomain:'randomsmallcompany.co.uk',
|
||||
secondLevelDomain: 'mail'
|
||||
});
|
||||
});
|
||||
|
||||
it("splits RFC compliant emails", function () {
|
||||
expect(mailcheck.splitEmail('"foo@bar"@example.com')).toEqual({
|
||||
address:'"foo@bar"',
|
||||
domain:'example.com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: 'example'
|
||||
|
||||
});
|
||||
expect(mailcheck.splitEmail('[email protected]')).toEqual({
|
||||
address:'containsnumbers1234567890',
|
||||
domain:'example.com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: 'example'
|
||||
});
|
||||
expect(mailcheck.splitEmail('[email protected]')).toEqual({
|
||||
address:'contains+symbol',
|
||||
domain:'example.com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: 'example'
|
||||
});
|
||||
expect(mailcheck.splitEmail('[email protected]')).toEqual({
|
||||
address:'contains-symbol',
|
||||
domain:'example.com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: 'example'
|
||||
});
|
||||
expect(mailcheck.splitEmail('[email protected]')).toEqual({
|
||||
address:'contains.symbol',
|
||||
domain:'domain.contains.symbol',
|
||||
topLevelDomain:'contains.symbol',
|
||||
secondLevelDomain: 'domain'
|
||||
});
|
||||
expect(mailcheck.splitEmail('"contains.and\ symbols"@example.com')).toEqual({
|
||||
address:'"contains.and\ symbols"',
|
||||
domain:'example.com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: 'example'
|
||||
});
|
||||
expect(mailcheck.splitEmail('"[email protected]"@example.com')).toEqual({
|
||||
address:'"[email protected]"',
|
||||
domain:'example.com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: 'example'
|
||||
});
|
||||
expect(mailcheck.splitEmail('"()<>[]:;@,\\\"!#$%&\'*+-/=?^_`{}|\ \ \ \ \ ~\ \ \ \ \ \ \ ?\ \ \ \ \ \ \ \ \ \ \ \ ^_`{}|~.a"@allthesymbols.com')).toEqual({
|
||||
address:'"()<>[]:;@,\\\"!#$%&\'*+-/=?^_`{}|\ \ \ \ \ ~\ \ \ \ \ \ \ ?\ \ \ \ \ \ \ \ \ \ \ \ ^_`{}|~.a"',
|
||||
domain:'allthesymbols.com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: 'allthesymbols'
|
||||
});
|
||||
expect(mailcheck.splitEmail('postbox@com')).toEqual({
|
||||
address:'postbox',
|
||||
domain:'com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: ''
|
||||
});
|
||||
});
|
||||
|
||||
it("returns false for email addresses that are not RFC compliant", function () {
|
||||
expect(mailcheck.splitEmail('example.com')).toBeFalsy();
|
||||
expect(mailcheck.splitEmail('abc.example.com')).toBeFalsy();
|
||||
expect(mailcheck.splitEmail('@example.com')).toBeFalsy();
|
||||
expect(mailcheck.splitEmail('test@')).toBeFalsy();
|
||||
});
|
||||
|
||||
it("trims spaces from the start and end of the string", function () {
|
||||
expect(mailcheck.splitEmail(' postbox@com')).toEqual({
|
||||
address:'postbox',
|
||||
domain:'com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: ''
|
||||
});
|
||||
expect(mailcheck.splitEmail('postbox@com ')).toEqual({
|
||||
address:'postbox',
|
||||
domain:'com',
|
||||
topLevelDomain:'com',
|
||||
secondLevelDomain: ''
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("mailcheck.findClosestDomain", function () {
|
||||
it("returns the most similar domain", function () {
|
||||
expect(mailcheck.findClosestDomain('google.com', domains)).toEqual('google.com');
|
||||
expect(mailcheck.findClosestDomain('gmail.com', domains)).toEqual('gmail.com');
|
||||
expect(mailcheck.findClosestDomain('emaildoman.com', domains)).toEqual('emaildomain.com');
|
||||
expect(mailcheck.findClosestDomain('gmsn.com', domains)).toEqual('msn.com');
|
||||
expect(mailcheck.findClosestDomain('gmaik.com', domains)).toEqual('gmail.com');
|
||||
});
|
||||
|
||||
it("returns the most similar second-level domain", function () {
|
||||
expect(mailcheck.findClosestDomain('hotmial', secondLevelDomains)).toEqual('hotmail');
|
||||
expect(mailcheck.findClosestDomain('tahoo', secondLevelDomains)).toEqual('yahoo');
|
||||
expect(mailcheck.findClosestDomain('livr', secondLevelDomains)).toEqual('live');
|
||||
expect(mailcheck.findClosestDomain('outllok', secondLevelDomains)).toEqual('outlook');
|
||||
});
|
||||
|
||||
it("returns the most similar top-level domain", function () {
|
||||
expect(mailcheck.findClosestDomain('cmo', topLevelDomains)).toEqual('com');
|
||||
expect(mailcheck.findClosestDomain('ogr', topLevelDomains)).toEqual('org');
|
||||
expect(mailcheck.findClosestDomain('ifno', topLevelDomains)).toEqual('info');
|
||||
expect(mailcheck.findClosestDomain('com.uk', topLevelDomains)).toEqual('co.uk');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Browser-only code below:
|
||||
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
describe("jquery.mailcheck", function () {
|
||||
var suggestedSpy, emptySpy;
|
||||
|
||||
beforeEach(function() {
|
||||
$('body').append('<div id="playground"></div>');
|
||||
|
||||
suggestedSpy = jasmine.createSpy();
|
||||
emptySpy = jasmine.createSpy();
|
||||
|
||||
$('#playground').append('<input type="text" id="test-input"/>');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
$('#playground').remove();
|
||||
});
|
||||
|
||||
it("calls the 'suggested' callback with the element and result when there's a suggestion", function () {
|
||||
$("#test-input").val('[email protected]').mailcheck({
|
||||
suggested: suggestedSpy,
|
||||
empty: emptySpy
|
||||
});
|
||||
|
||||
expect(suggestedSpy).toHaveBeenCalledWith($("#test-input"),{
|
||||
address: 'test',
|
||||
domain: 'gmail.com',
|
||||
full: '[email protected]'
|
||||
});
|
||||
|
||||
expect(emptySpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls the 'empty' callback with the element when there's no suggestion", function () {
|
||||
$("#test-input").val('[email protected]').mailcheck({
|
||||
suggested: suggestedSpy,
|
||||
empty: emptySpy
|
||||
});
|
||||
|
||||
expect(suggestedSpy).not.toHaveBeenCalled();
|
||||
|
||||
expect(emptySpy).toHaveBeenCalledWith($("#test-input"));
|
||||
});
|
||||
});
|
||||
});
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Jasmine Test Runner</title>
|
||||
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.2.0/jasmine.css">
|
||||
<script type="text/javascript" src="lib/jasmine-1.2.0/jasmine.js"></script>
|
||||
<script type="text/javascript" src="lib/jasmine-1.2.0/jasmine-html.js"></script>
|
||||
<script type="text/javascript" src="lib/jquery.js"></script>
|
||||
<script type="text/javascript" src="lib/console-runner.js"></script>
|
||||
|
||||
<!-- SOURCE FILES -->
|
||||
<script type="text/javascript" src="../src/mailcheck.js"></script>
|
||||
|
||||
<!-- TEST FILES -->
|
||||
<script type="text/javascript" src="mailcheckSpec.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
var console_reporter = new jasmine.ConsoleReporter()
|
||||
jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
|
||||
jasmine.getEnv().addReporter(console_reporter);
|
||||
jasmine.getEnv().execute();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Mailcheck https://github.com/mailcheck/mailcheck
|
||||
* Author
|
||||
* Derrick Ko (@derrickko)
|
||||
*
|
||||
* Released under the MIT License.
|
||||
*
|
||||
* v 1.1.1
|
||||
*/
|
||||
|
||||
var Mailcheck = {
|
||||
domainThreshold: 2,
|
||||
secondLevelThreshold: 2,
|
||||
topLevelThreshold: 2,
|
||||
|
||||
defaultDomains: ['msn.com', 'bellsouth.net',
|
||||
'telus.net', 'comcast.net', 'optusnet.com.au',
|
||||
'earthlink.net', 'qq.com', 'sky.com', 'icloud.com',
|
||||
'mac.com', 'sympatico.ca', 'googlemail.com',
|
||||
'att.net', 'xtra.co.nz', 'web.de',
|
||||
'cox.net', 'gmail.com', 'ymail.com',
|
||||
'aim.com', 'rogers.com', 'verizon.net',
|
||||
'rocketmail.com', 'google.com', 'optonline.net',
|
||||
'sbcglobal.net', 'aol.com', 'me.com', 'btinternet.com',
|
||||
'charter.net', 'shaw.ca'],
|
||||
|
||||
defaultSecondLevelDomains: ["yahoo", "hotmail", "mail", "live", "outlook", "gmx"],
|
||||
|
||||
defaultTopLevelDomains: ["com", "com.au", "com.tw", "ca", "co.nz", "co.uk", "de",
|
||||
"fr", "it", "ru", "net", "org", "edu", "gov", "jp", "nl", "kr", "se", "eu",
|
||||
"ie", "co.il", "us", "at", "be", "dk", "hk", "es", "gr", "ch", "no", "cz",
|
||||
"in", "net", "net.au", "info", "biz", "mil", "co.jp", "sg", "hu"],
|
||||
|
||||
run: function(opts) {
|
||||
opts.domains = opts.domains || Mailcheck.defaultDomains;
|
||||
opts.secondLevelDomains = opts.secondLevelDomains || Mailcheck.defaultSecondLevelDomains;
|
||||
opts.topLevelDomains = opts.topLevelDomains || Mailcheck.defaultTopLevelDomains;
|
||||
opts.distanceFunction = opts.distanceFunction || Mailcheck.sift3Distance;
|
||||
|
||||
var defaultCallback = function(result){ return result };
|
||||
var suggestedCallback = opts.suggested || defaultCallback;
|
||||
var emptyCallback = opts.empty || defaultCallback;
|
||||
|
||||
var result = Mailcheck.suggest(Mailcheck.encodeEmail(opts.email), opts.domains, opts.secondLevelDomains, opts.topLevelDomains, opts.distanceFunction);
|
||||
|
||||
return result ? suggestedCallback(result) : emptyCallback()
|
||||
},
|
||||
|
||||
suggest: function(email, domains, secondLevelDomains, topLevelDomains, distanceFunction) {
|
||||
email = email.toLowerCase();
|
||||
|
||||
var emailParts = this.splitEmail(email);
|
||||
|
||||
if (secondLevelDomains && topLevelDomains) {
|
||||
// If the email is a valid 2nd-level + top-level, do not suggest anything.
|
||||
if (secondLevelDomains.indexOf(emailParts.secondLevelDomain) !== -1 && topLevelDomains.indexOf(emailParts.topLevelDomain) !== -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var closestDomain = this.findClosestDomain(emailParts.domain, domains, distanceFunction, this.domainThreshold);
|
||||
|
||||
if (closestDomain) {
|
||||
if (closestDomain == emailParts.domain) {
|
||||
// The email address exactly matches one of the supplied domains; do not return a suggestion.
|
||||
return false;
|
||||
} else {
|
||||
// The email address closely matches one of the supplied domains; return a suggestion
|
||||
return { address: emailParts.address, domain: closestDomain, full: emailParts.address + "@" + closestDomain };
|
||||
}
|
||||
}
|
||||
|
||||
// The email address does not closely match one of the supplied domains
|
||||
var closestSecondLevelDomain = this.findClosestDomain(emailParts.secondLevelDomain, secondLevelDomains, distanceFunction, this.secondLevelThreshold);
|
||||
var closestTopLevelDomain = this.findClosestDomain(emailParts.topLevelDomain, topLevelDomains, distanceFunction, this.topLevelThreshold);
|
||||
|
||||
if (emailParts.domain) {
|
||||
var closestDomain = emailParts.domain;
|
||||
var rtrn = false;
|
||||
|
||||
if(closestSecondLevelDomain && closestSecondLevelDomain != emailParts.secondLevelDomain) {
|
||||
// The email address may have a mispelled second-level domain; return a suggestion
|
||||
closestDomain = closestDomain.replace(emailParts.secondLevelDomain, closestSecondLevelDomain);
|
||||
rtrn = true;
|
||||
}
|
||||
|
||||
if(closestTopLevelDomain && closestTopLevelDomain != emailParts.topLevelDomain) {
|
||||
// The email address may have a mispelled top-level domain; return a suggestion
|
||||
closestDomain = closestDomain.replace(emailParts.topLevelDomain, closestTopLevelDomain);
|
||||
rtrn = true;
|
||||
}
|
||||
|
||||
if (rtrn == true) {
|
||||
return { address: emailParts.address, domain: closestDomain, full: emailParts.address + "@" + closestDomain };
|
||||
}
|
||||
}
|
||||
|
||||
/* The email address exactly matches one of the supplied domains, does not closely
|
||||
* match any domain and does not appear to simply have a mispelled top-level domain,
|
||||
* or is an invalid email address; do not return a suggestion.
|
||||
*/
|
||||
return false;
|
||||
},
|
||||
|
||||
findClosestDomain: function(domain, domains, distanceFunction, threshold) {
|
||||
threshold = threshold || this.topLevelThreshold;
|
||||
var dist;
|
||||
var minDist = 99;
|
||||
var closestDomain = null;
|
||||
|
||||
if (!domain || !domains) {
|
||||
return false;
|
||||
}
|
||||
if(!distanceFunction) {
|
||||
distanceFunction = this.sift3Distance;
|
||||
}
|
||||
|
||||
for (var i = 0; i < domains.length; i++) {
|
||||
if (domain === domains[i]) {
|
||||
return domain;
|
||||
}
|
||||
dist = distanceFunction(domain, domains[i]);
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
closestDomain = domains[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (minDist <= threshold && closestDomain !== null) {
|
||||
return closestDomain;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
sift3Distance: function(s1, s2) {
|
||||
// sift3: http://siderite.blogspot.com/2007/04/super-fast-and-accurate-string-distance.html
|
||||
if (s1 == null || s1.length === 0) {
|
||||
if (s2 == null || s2.length === 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return s2.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (s2 == null || s2.length === 0) {
|
||||
return s1.length;
|
||||
}
|
||||
|
||||
var c = 0;
|
||||
var offset1 = 0;
|
||||
var offset2 = 0;
|
||||
var lcs = 0;
|
||||
var maxOffset = 5;
|
||||
|
||||
while ((c + offset1 < s1.length) && (c + offset2 < s2.length)) {
|
||||
if (s1.charAt(c + offset1) == s2.charAt(c + offset2)) {
|
||||
lcs++;
|
||||
} else {
|
||||
offset1 = 0;
|
||||
offset2 = 0;
|
||||
for (var i = 0; i < maxOffset; i++) {
|
||||
if ((c + i < s1.length) && (s1.charAt(c + i) == s2.charAt(c))) {
|
||||
offset1 = i;
|
||||
break;
|
||||
}
|
||||
if ((c + i < s2.length) && (s1.charAt(c) == s2.charAt(c + i))) {
|
||||
offset2 = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
c++;
|
||||
}
|
||||
return (s1.length + s2.length) /2 - lcs;
|
||||
},
|
||||
|
||||
splitEmail: function(email) {
|
||||
var parts = email.trim().split('@');
|
||||
|
||||
if (parts.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
if (parts[i] === '') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var domain = parts.pop();
|
||||
var domainParts = domain.split('.');
|
||||
var sld = '';
|
||||
var tld = '';
|
||||
|
||||
if (domainParts.length == 0) {
|
||||
// The address does not have a top-level domain
|
||||
return false;
|
||||
} else if (domainParts.length == 1) {
|
||||
// The address has only a top-level domain (valid under RFC)
|
||||
tld = domainParts[0];
|
||||
} else {
|
||||
// The address has a domain and a top-level domain
|
||||
sld = domainParts[0];
|
||||
for (var i = 1; i < domainParts.length; i++) {
|
||||
tld += domainParts[i] + '.';
|
||||
}
|
||||
tld = tld.substring(0, tld.length - 1);
|
||||
}
|
||||
|
||||
return {
|
||||
topLevelDomain: tld,
|
||||
secondLevelDomain: sld,
|
||||
domain: domain,
|
||||
address: parts.join('@')
|
||||
}
|
||||
},
|
||||
|
||||
// Encode the email address to prevent XSS but leave in valid
|
||||
// characters, following this official spec:
|
||||
// http://en.wikipedia.org/wiki/Email_address#Syntax
|
||||
encodeEmail: function(email) {
|
||||
var result = encodeURI(email);
|
||||
result = result.replace('%20', ' ').replace('%25', '%').replace('%5E', '^')
|
||||
.replace('%60', '`').replace('%7B', '{').replace('%7C', '|')
|
||||
.replace('%7D', '}');
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Export the mailcheck object if we're in a CommonJS env (e.g. Node).
|
||||
// Modeled off of Underscore.js.
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = Mailcheck;
|
||||
}
|
||||
|
||||
// Support AMD style definitions
|
||||
// Based on jQuery (see http://stackoverflow.com/a/17954882/1322410)
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define("mailcheck", [], function() {
|
||||
return Mailcheck;
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && window.jQuery) {
|
||||
(function($){
|
||||
$.fn.mailcheck = function(opts) {
|
||||
var self = this;
|
||||
if (opts.suggested) {
|
||||
var oldSuggested = opts.suggested;
|
||||
opts.suggested = function(result) {
|
||||
oldSuggested(self, result);
|
||||
};
|
||||
}
|
||||
|
||||
if (opts.empty) {
|
||||
var oldEmpty = opts.empty;
|
||||
opts.empty = function() {
|
||||
oldEmpty.call(null, self);
|
||||
};
|
||||
}
|
||||
|
||||
opts.email = this.val();
|
||||
Mailcheck.run(opts);
|
||||
}
|
||||
})(jQuery);
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
/*! mailcheck v1.1.1 @licence MIT */var Mailcheck={domainThreshold:2,secondLevelThreshold:2,topLevelThreshold:2,defaultDomains:["msn.com","bellsouth.net","telus.net","comcast.net","optusnet.com.au","earthlink.net","qq.com","sky.com","icloud.com","mac.com","sympatico.ca","googlemail.com","att.net","xtra.co.nz","web.de","cox.net","gmail.com","ymail.com","aim.com","rogers.com","verizon.net","rocketmail.com","google.com","optonline.net","sbcglobal.net","aol.com","me.com","btinternet.com","charter.net","shaw.ca"],defaultSecondLevelDomains:["yahoo","hotmail","mail","live","outlook","gmx"],defaultTopLevelDomains:["com","com.au","com.tw","ca","co.nz","co.uk","de","fr","it","ru","net","org","edu","gov","jp","nl","kr","se","eu","ie","co.il","us","at","be","dk","hk","es","gr","ch","no","cz","in","net","net.au","info","biz","mil","co.jp","sg","hu"],run:function(a){a.domains=a.domains||Mailcheck.defaultDomains,a.secondLevelDomains=a.secondLevelDomains||Mailcheck.defaultSecondLevelDomains,a.topLevelDomains=a.topLevelDomains||Mailcheck.defaultTopLevelDomains,a.distanceFunction=a.distanceFunction||Mailcheck.sift3Distance;var b=function(a){return a},c=a.suggested||b,d=a.empty||b,e=Mailcheck.suggest(Mailcheck.encodeEmail(a.email),a.domains,a.secondLevelDomains,a.topLevelDomains,a.distanceFunction);return e?c(e):d()},suggest:function(a,b,c,d,e){a=a.toLowerCase();var f=this.splitEmail(a);if(c&&d&&-1!==c.indexOf(f.secondLevelDomain)&&-1!==d.indexOf(f.topLevelDomain))return!1;var g=this.findClosestDomain(f.domain,b,e,this.domainThreshold);if(g)return g==f.domain?!1:{address:f.address,domain:g,full:f.address+"@"+g};var h=this.findClosestDomain(f.secondLevelDomain,c,e,this.secondLevelThreshold),i=this.findClosestDomain(f.topLevelDomain,d,e,this.topLevelThreshold);if(f.domain){var g=f.domain,j=!1;if(h&&h!=f.secondLevelDomain&&(g=g.replace(f.secondLevelDomain,h),j=!0),i&&i!=f.topLevelDomain&&(g=g.replace(f.topLevelDomain,i),j=!0),1==j)return{address:f.address,domain:g,full:f.address+"@"+g}}return!1},findClosestDomain:function(a,b,c,d){d=d||this.topLevelThreshold;var e,f=99,g=null;if(!a||!b)return!1;c||(c=this.sift3Distance);for(var h=0;h<b.length;h++){if(a===b[h])return a;e=c(a,b[h]),f>e&&(f=e,g=b[h])}return d>=f&&null!==g?g:!1},sift3Distance:function(a,b){if(null==a||0===a.length)return null==b||0===b.length?0:b.length;if(null==b||0===b.length)return a.length;for(var c=0,d=0,e=0,f=0,g=5;c+d<a.length&&c+e<b.length;){if(a.charAt(c+d)==b.charAt(c+e))f++;else{d=0,e=0;for(var h=0;g>h;h++){if(c+h<a.length&&a.charAt(c+h)==b.charAt(c)){d=h;break}if(c+h<b.length&&a.charAt(c)==b.charAt(c+h)){e=h;break}}}c++}return(a.length+b.length)/2-f},splitEmail:function(a){var b=a.trim().split("@");if(b.length<2)return!1;for(var c=0;c<b.length;c++)if(""===b[c])return!1;var d=b.pop(),e=d.split("."),f="",g="";if(0==e.length)return!1;if(1==e.length)g=e[0];else{f=e[0];for(var c=1;c<e.length;c++)g+=e[c]+".";g=g.substring(0,g.length-1)}return{topLevelDomain:g,secondLevelDomain:f,domain:d,address:b.join("@")}},encodeEmail:function(a){var b=encodeURI(a);return b=b.replace("%20"," ").replace("%25","%").replace("%5E","^").replace("%60","`").replace("%7B","{").replace("%7C","|").replace("%7D","}")}};"undefined"!=typeof module&&module.exports&&(module.exports=Mailcheck),"function"==typeof define&&define.amd&&define("mailcheck",[],function(){return Mailcheck}),"undefined"!=typeof window&&window.jQuery&&!function(a){a.fn.mailcheck=function(a){var b=this;if(a.suggested){var c=a.suggested;a.suggested=function(a){c(b,a)}}if(a.empty){var d=a.empty;a.empty=function(){d.call(null,b)}}a.email=this.val(),Mailcheck.run(a)}}(jQuery);
|
||||
Reference in New Issue
Block a user