<commit_before>/*!
 * simplate-js v1.1
 * Copyright 2010, Michael Morton
 *
 * MIT Licensed - See LICENSE.txt
 */
(function() {
    var trimRE = /^\s+|\s+$/g,
        escQuoteRE = /'/g,
        escNewLineRE = /\n/g,
        entAmpRE = /&/g,
        entLtRE = /</g,
        entGtRE = />/g,
        entQuotRE = /"/g,
        cache = {},
        options = {
            tags: {
                begin: "{%",
                end: "%}"
            }
        };

    var mix = function(a, b, c) {
        if (c) for (var n in c) a[n] = c[n];
        if (b) for (var n in b) a[n] = b[n];
        return a;
    };

    var encode = function(val) {
        if (typeof val !== 'string') return val;

        return val
            .replace(entAmpRE, '&')
            .replace(entLtRE, '<')
            .replace(entGtRE, '>')
            .replace(entQuotRE, '"');
    };

    var escape = function(val) {
        return val
            .replace(escQuoteRE, '\\\'')
            .replace(escNewLineRE, '\\n');
    };

    var trim = function(val) {
        return val.replace(trimRE, '');
    };

    var make = function(markup, o) {
        if (markup.join) markup = markup.join('');
        if (cache[markup]) return cache[markup];

        var o = mix({}, o, options);

        if ('is,ie'.split(/(,)/).length !== 3)
        {
            var fragments = [];
            var a = markup.split(o.tags.begin);
            for (var i = 0; i < a.length; i++)
                fragments.push.apply(fragments, a[i].split(o.tags.end));
        }
        else
        {
            var regex = new RegExp(o.tags.begin + '(.*?)' + o.tags.end);
            var fragments = markup.split(regex);
        }

        /* code fragments */
        for (var i = 1; i < fragments.length; i += 2)
        {
            if (fragments[i].length > 0)
            {
                var control = fragments[i].charAt(0),
                    source = fragments[i].substr(1);

                switch (control)
                {
                    case '=':
                        fragments[i] = '__p(' + source + ');';
                        break;
                    case ':':
                        fragments[i] = '__p(__s.encode(' + source + '));';
                        break;
                    case '!':
                        fragments[i] = '__p(' + trim(source) + '.apply(__v, this));';
                        break;
                    default:
                        break;
                }
            }
        }

        for (var i = 0; i < fragments.length; i += 2)
            fragments[i] = '__p(\'' + escape(fragments[i]) + '\');';

        fragments.unshift(
            'var __r = [], $ = __v, $$ = this, __s = Simplate, __p = function() { __r.push.apply(__r, arguments); };',
            'with ($ || {}) {'
        );

        fragments.push(
            '}',
            'return __r.join(\'\');'
        );

        var fn;

        try
        {
            fn = new Function('__v', fragments.join(''));
        }
        catch (e)
        {
            fn = function(values) { return e.message; };
        }

        return (cache[markup] = fn);
    };

    S = this.Simplate = function(markup, o) {
        this.fn = make(markup, o);
    };

    mix(S, {
        options: options,
        encode: encode
    });

    mix(S.prototype, {
        apply: function(data, scope) {
            return this.fn.call(scope || this, data);
        }
    });
})();<commit_msg>various updates.<commit_after>/*!
 * simplate-js v1.1
 * Copyright 2010, Michael Morton
 *
 * MIT Licensed - See LICENSE.txt
 */
(function() {
    var trimRE = /^\s+|\s+$/g,
        escQuoteRE = /'/g,
        escNewLineRE = /\n/g,
        entAmpRE = /&/g,
        entLtRE = /</g,
        entGtRE = />/g,
        entQuotRE = /"/g,
        cache = {},
        options = {
            tags: {
                begin: "{%",
                end: "%}"
            },
            allowWith: false
        };

    var mix = function(a, b, c) {
        if (c) for (var n in c) a[n] = c[n];
        if (b) for (var n in b) a[n] = b[n];
        return a;
    };

    var encode = function(val) {
        if (typeof val !== 'string') return val;

        return val
            .replace(entAmpRE, '&')
            .replace(entLtRE, '<')
            .replace(entGtRE, '>')
            .replace(entQuotRE, '"');
    };

    var escape = function(val) {
        return val
            .replace(escQuoteRE, '\\\'')
            .replace(escNewLineRE, '\\n');
    };

    var trim = function(val) {
        return val.replace(trimRE, '');
    };

    var make = function(markup, o) {
        if (markup.join) markup = markup.join('');
        if (cache[markup]) return cache[markup];

        var o = mix({}, o, options);

        if ('is,ie'.split(/(,)/).length !== 3)
        {
            var fragments = [];
            var a = markup.split(o.tags.begin);
            for (var i = 0; i < a.length; i++)
                fragments.push.apply(fragments, a[i].split(o.tags.end));
        }
        else
        {
            var regex = new RegExp(o.tags.begin + '(.*?)' + o.tags.end);
            var fragments = markup.split(regex);
        }

        /* code fragments */
        for (var i = 1; i < fragments.length; i += 2)
        {
            if (fragments[i].length > 0)
            {
                var control = fragments[i].charAt(0),
                    source = fragments[i].substr(1);

                switch (control)
                {
                    case '=':
                        fragments[i] = '__p(' + source + ');';
                        break;
                    case ':':
                        fragments[i] = '__p(__s.encode(' + source + '));';
                        break;
                    case '!':
                        fragments[i] = '__p(' + trim(source) + '.apply(__v, this));';
                        break;
                    default:
                        break;
                }
            }
        }

        for (var i = 0; i < fragments.length; i += 2)
            fragments[i] = '__p(\'' + escape(fragments[i]) + '\');';

        fragments.unshift(
            'var __r = [], $ = __v, $$ = this, __s = Simplate, __p = function() { __r.push.apply(__r, arguments); };',
            options.allowWith ? 'with ($ || {}) {' : ''
        );

        fragments.push(
            options.allowWith ? '}': '',
            'return __r.join(\'\');'
        );

        var fn;

        try
        {
            fn = new Function('__v', fragments.join(''));
        }
        catch (e)
        {
            fn = function(values) { return e.message; };
        }

        return (cache[markup] = fn);
    };

    S = this.Simplate = function(markup, o) {
        this.fn = make(markup, o);
    };

    mix(S, {
        options: options,
        encode: encode
    });

    mix(S.prototype, {
        apply: function(data, scope) {
            return this.fn.call(scope || this, data);
        }
    });
})();<|endoftext|>


<commit_before>#!/usr/bin/env node
var N3Parser = require('../lib/N3Parser.js'),
    N3Store = require('../lib/N3Store.js');
var fs = require('fs'),
    path = require('path'),
    request = require('request'),
    exec = require('child_process').exec,
    colors = require('colors'),
    async = require('async');

// Should the tests run in parallel?
var parallel = false;

// Path to the tests and the tests' manifest
var testPath = "http://www.w3.org/2013/TurtleTests/",
    manifest = "manifest.ttl";

// Prefixes
var prefixes = {
  mf: "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
  rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
  rdfs: "http://www.w3.org/2000/01/rdf-schema#",
  rdft: "http://www.w3.org/ns/rdftest#",
  dc: "http://purl.org/dc/terms/",
  doap: "http://usefulinc.com/ns/doap#",
  earl: "http://www.w3.org/ns/earl#",
  foaf: "http://xmlns.com/foaf/0.1/",
  xsd: "http://www.w3.org/2001/XMLSchema#",
  manifest: testPath + manifest + '#',
};

// List predicates
var first = prefixes.rdf + "first",
    rest = prefixes.rdf + "rest",
    nil = prefixes.rdf + "nil";

// Create the folders that will contain the spec files and results
var specFolder = __dirname,
    testFolder = path.join(specFolder, 'turtle'),
    outputFolder = path.join(testFolder, 'results');
[specFolder, testFolder, outputFolder].forEach(function (folder) {
  if (!fs.existsSync(folder))
    fs.mkdirSync(folder);
});

runSpecTest();

/*************************************************

  Test suite execution

**************************************************/

// Fetches the manifest, executes all tests, and reports results
function runSpecTest() {
  console.log("Turtle Terse RDF Triple Language Test Cases".bold);
  async.waterfall([
    // Fetch and parse the manifest
    fetch.bind(null, manifest),
    parseManifest,

    // Perform the tests in the manifest
    function performTests(manifest, callback) {
      async[parallel ? 'map' : 'mapSeries'](manifest.tests, function (test, callback) {
        async.parallel({ actionTurtle: fetch.bind(null, test.action),
                         resultTurtle: fetch.bind(null, test.result) },
          function (err, results) {
            performTest(test, results.actionTurtle, callback);
          });
      },

      // Show the summary of the performed tests
      function showSummary(error, tests) {
        var score = tests.reduce(function (sum, test) { return sum + test.success; }, 0);
        console.log(("* passed " + score + " out of " + manifest.tests.length + " tests").bold);
        callback(error, tests);
      });
    },

    // Generate the EARL report
    generateEarlReport,
  ],
  function (error) {
    if (error) {
      console.error("ERROR".red);
      console.error(error.red);
      process.exit(1);
    }
  });
}

// Parses the tests manifest into tests
function parseManifest(manifestContents, callback) {
  var manifest = {},
      testStore = new N3Store();

  // Parse the manifest into triples
  new N3Parser().parse(manifestContents, function (err, triple) {
    if (err)
      return callback(err);

    // Store triples until there are no more
    if (triple)
      return testStore.addTriple(triple.subject, triple.predicate, triple.object);

    // Once all triples are there, get the first item of the test list
    var tests = manifest.tests = [],
        itemHead = testStore.find('', prefixes.mf + 'entries', null)[0].object;
    // Loop through all test items
    while (itemHead && itemHead !== nil) {
      // Find and store the item's properties
      var itemValue = testStore.find(itemHead, first, null)[0].object,
          itemTriples = testStore.find(itemValue, null, null),
          test = { id: itemValue.replace(/^#/, '') };
      itemTriples.forEach(function (triple) {
        var propertyMatch = triple.predicate.match(/#(.+)/);
        if (propertyMatch)
          test[propertyMatch[1]] = triple.object;
      });
      tests.push(test);

      // Find the next test item
      itemHead = testStore.find(itemHead, rest, null)[0].object;
    }
    return callback(null, manifest);
  });
}

// Fetches and caches the specified file, or retrieves it from disk
function fetch(testFile, callback) {
  if (!testFile)
    return callback(null, null);

  var localFile = path.join(testFolder, testFile);
  fs.exists(localFile, function (exists) {
    if (exists)
      fs.readFile(localFile, 'utf8', callback);
    else
      request.get(testPath + testFile,
                  function (error, response, body) { callback(error, body); })
             .pipe(fs.createWriteStream(localFile));
  });
}

/*************************************************

  Individual test execution

**************************************************/

// Performs the specified test by parsing the specified Turtle document
function performTest(test, actionTurtle, callback) {
  // Create the results file
  var resultFile = path.join(outputFolder, test.action.replace(/\.ttl$/, '.nt')),
      resultStream = fs.createWriteStream(resultFile);

  resultStream.once('open', function () {
    // Try to parse the specified document
    var config = { documentURI: testPath + test.action };
    new N3Parser(config).parse(actionTurtle,
      function (error, triple) {
        if (error)
          test.error = error;

        // Write the triple to the results file, or end if none are left
        if (triple)
          resultStream.write(toNTriple(triple));
        else
          resultStream.end();
      });
  });

  // Verify the result if the result has been written
  resultStream.once('close', function () {
    verifyResult(test, resultFile, test.result && (testFolder + test.result), callback);
  });
}

// Verifies and reports the test result
function verifyResult(test, resultFile, correctFile, callback) {
  // Negative tests are successful if an error occurred
  var negativeTest = /TestTurtleNegative/.test(test.type);
  if (negativeTest) {
    displayResult(null, !!test.error);
  }
  // Positive tests are successful if the results are equal,
  // or if the correct solution is not given but no error occurred
  else {
    if (!correctFile)
      displayResult(null, !test.error);
    else if (resultFile)
      compareGraphs(resultFile, correctFile, displayResult);
    else
      displayResult(null, false);
  }

  // Display the test result
  function displayResult(error, success, comparison) {
    console.log(unString(test.name).bold + ':', unString(test.comment),
                (success ? 'OK'.green : 'FAIL'.red).bold);
    if (!success) {
      console.log((correctFile ? fs.readFileSync(correctFile, 'utf8') : '(empty)').grey);
      console.log('  was expected, but got'.bold.grey);
      console.log((resultFile ? fs.readFileSync(resultFile, 'utf8') : '(empty)').grey);
      console.log(('  error: '.bold + (test.error || '(none)')).grey);
      if (comparison)
        console.log(('  comparison: ' + comparison).grey);
    }
    test.success = success;
    callback(null, test);
  }
}

// Verifies whether the two graphs are equal
function compareGraphs(actual, expected, callback) {
  // Try a full-text comparison (fastest)
  async.parallel({
    actualContents: fs.readFile.bind(fs, actual, 'utf8'),
    expectedContents: fs.readFile.bind(fs, expected, 'utf8'),
  },
  function (error, results) {
    // If the full-text comparison was successful, graphs are certainly equal
    if (results.actualContents === results.expectedContents)
      callback(null, true);
    // If not, we check for proper graph equality with SWObjects
    else
      exec('sparql -d ' + expected + ' --compare ' + actual, function (error, stdout, stderr) {
        callback(error, /^matched\s*$/.test(stdout), stdout);
      });
  });
}

/*************************************************

  Conversion routines

**************************************************/

// Converts the triple to NTriples format (primitive and incomplete)
function toNTriple(triple) {
  var subject = triple.subject,
      predicate = triple.predicate,
      object = triple.object;

  if (/^".*"$/.test(object))
    object = escapeString(object);
  else
    object = escape(object);

  return (subject.match(/^_/)   ? subject   : '<' + subject   + '>') + ' ' +
         (predicate.match(/^_/) ? predicate : '<' + predicate + '>') + ' ' +
         (object.match(/^_|^"/) ? object    : '<' + object +    '>') + ' .\n';
}

// Removes the quotes around a string
function unString(value) {
  return value ? value.replace(/^("""|")(.*)\1$/, '$2') : '';
}

// Escapes unicode characters in a URI
function escape(value) {
  var result = '';

  // Add all characters, converting to an unicode escape code if necessary
  for (var i = 0; i < value.length; i++) {
    var code = value.charCodeAt(i);
    if (code >= 32 && code < 128) {
      result += value[i];
    }
    else {
      var hexCode = code.toString(16);
      while (hexCode.length < 4)
        hexCode = '0' + hexCode;
      result += '\\u' + hexCode;
    }
  }

  // Convert surrogate pairs to actual unicode (http://mathiasbynens.be/notes/javascript-encoding)
  result = result.replace(/\\u([a-z0-9]{4})\\u([a-z0-9]{4})/gi, function (all, high, low) {
    high = parseInt(high, 16);
    low = parseInt(low, 16);
    if (high >= 0xD800 && high <= 0xDBFF && low >= 0xDC00 && low <= 0xDFFF) {
      var result = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000;
      result = result.toString(16);
      while (result.length < 8)
        result = '0' + result;
      return '\\U' + result;
    }
    return all;
  });

  return result;
}

// Escapes characters in a string
function escapeString(value) {
  value = value.replace(/\\/g, '\\\\');
  value = escape(unString(value));
  value = value.replace(/"/g, '\\"');
  return '"' + value + '"';
}

/*************************************************

  EARL report generation

**************************************************/

var homepage = 'https://github.com/RubenVerborgh/N3.js',
    application = homepage + '#n3js',
    developer = 'http://ruben.verborgh.org/#me';

function generateEarlReport(tests, callback) {
  // Create the report file
  var reportFile = path.join(outputFolder, 'earl-report.ttl'),
      report = fs.createWriteStream(reportFile),
      date = new Date().toISOString();

  report.once('open', function () {
    for (var prefix in prefixes)
      writeln('@prefix ', prefix, ': <', prefixes[prefix], '>.');
    writeln();

    writeln('<> foaf:primaryTopic <', application, '>;');
    writeln('  dc:issued "', date, '"^^xsd:dateTime;');
    writeln('  foaf:maker <', developer, '>.');
    writeln();

    writeln('<', application, '> a earl:Software, earl:TestSubject, doap:Project;');
    writeln('  doap:name "N3.js";');
    writeln('  doap:homepage <', homepage, '>;');
    writeln('  doap:license <http://opensource.org/licenses/MIT>;');
    writeln('  doap:programming-language "JavaScript";');
    writeln('  doap:implements <http://www.w3.org/TR/turtle/>;');
    writeln('  doap:category <http://dbpedia.org/resource/Resource_Description_Framework>;');
    writeln('  doap:download-page <https://npmjs.org/package/n3>;');
    writeln('  doap:bug-database <', homepage, '/issues>;');
    writeln('  doap:blog <http://ruben.verborgh.org/blog/>;');
    writeln('  doap:developer <', developer, '>;');
    writeln('  doap:maintainer <', developer, '>;');
    writeln('  doap:documenter <', developer, '>;');
    writeln('  doap:maker <', developer, '>;');
    writeln('  dc:title "N3.js";');
    writeln('  dc:description "N3.js is an asynchronous, streaming Turtle parser for JavaScript."@en;');
    writeln('  dc:creator <', developer, '>.');
    writeln();

    writeln('<', developer, '> a foaf:Person, earl:Assertor;');
    writeln('  foaf:name "Ruben Verborgh";');
    writeln('  foaf:homepage <http://ruben.verborgh.org/>;');
    writeln('  foaf:primaryTopicOf <http://ruben.verborgh.org/profile/>;');
    writeln('  rdfs:isDefinedBy <http://ruben.verborgh.org/profile/>.');

    tests.forEach(function (test) {
      writeln();
      writeln('manifest:', test.id, ' a earl:TestCriterion, earl:TestCase;');
      writeln('  dc:title ', escapeString(unString(test.name)), ';');
      writeln('  dc:description ', escapeString(unString(test.comment)), ';');
      writeln('  mf:action <', testPath, test.action, '>;');
      if (test.result)
        writeln('  mf:result <', testPath, test.result, '>;');
      writeln('  earl:assertions (');
      writeln('     [ a earl:Assertion;');
      writeln('       earl:assertedBy <', developer, '>;');
      writeln('       earl:test manifest:', test.id, ';');
      writeln('       earl:subject <', application, '>;');
      writeln('       earl:mode earl:automatic;');
      writeln('       earl:result [ a earl:TestResult; ',
                        'earl:outcome earl:', (test.success ? 'passed' : 'failed'), '; ',
                        'dc:date "', date, '"^^xsd:dateTime',
                      ' ]]');
      writeln('  ).');
    });

    report.end();
  });

  report.once('close', callback);

  function writeln() {
    for(var i = 0; i < arguments.length; i++)
      report.write(arguments[i]);
    report.write('\n');
  }
}
<commit_msg>A doap:description to EARL report.<commit_after>#!/usr/bin/env node
var N3Parser = require('../lib/N3Parser.js'),
    N3Store = require('../lib/N3Store.js');
var fs = require('fs'),
    path = require('path'),
    request = require('request'),
    exec = require('child_process').exec,
    colors = require('colors'),
    async = require('async');

// Should the tests run in parallel?
var parallel = false;

// Path to the tests and the tests' manifest
var testPath = "http://www.w3.org/2013/TurtleTests/",
    manifest = "manifest.ttl";

// Prefixes
var prefixes = {
  mf: "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
  rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
  rdfs: "http://www.w3.org/2000/01/rdf-schema#",
  rdft: "http://www.w3.org/ns/rdftest#",
  dc: "http://purl.org/dc/terms/",
  doap: "http://usefulinc.com/ns/doap#",
  earl: "http://www.w3.org/ns/earl#",
  foaf: "http://xmlns.com/foaf/0.1/",
  xsd: "http://www.w3.org/2001/XMLSchema#",
  manifest: testPath + manifest + '#',
};

// List predicates
var first = prefixes.rdf + "first",
    rest = prefixes.rdf + "rest",
    nil = prefixes.rdf + "nil";

// Create the folders that will contain the spec files and results
var specFolder = __dirname,
    testFolder = path.join(specFolder, 'turtle'),
    outputFolder = path.join(testFolder, 'results');
[specFolder, testFolder, outputFolder].forEach(function (folder) {
  if (!fs.existsSync(folder))
    fs.mkdirSync(folder);
});

runSpecTest();

/*************************************************

  Test suite execution

**************************************************/

// Fetches the manifest, executes all tests, and reports results
function runSpecTest() {
  console.log("Turtle Terse RDF Triple Language Test Cases".bold);
  async.waterfall([
    // Fetch and parse the manifest
    fetch.bind(null, manifest),
    parseManifest,

    // Perform the tests in the manifest
    function performTests(manifest, callback) {
      async[parallel ? 'map' : 'mapSeries'](manifest.tests, function (test, callback) {
        async.parallel({ actionTurtle: fetch.bind(null, test.action),
                         resultTurtle: fetch.bind(null, test.result) },
          function (err, results) {
            performTest(test, results.actionTurtle, callback);
          });
      },

      // Show the summary of the performed tests
      function showSummary(error, tests) {
        var score = tests.reduce(function (sum, test) { return sum + test.success; }, 0);
        console.log(("* passed " + score + " out of " + manifest.tests.length + " tests").bold);
        callback(error, tests);
      });
    },

    // Generate the EARL report
    generateEarlReport,
  ],
  function (error) {
    if (error) {
      console.error("ERROR".red);
      console.error(error.red);
      process.exit(1);
    }
  });
}

// Parses the tests manifest into tests
function parseManifest(manifestContents, callback) {
  var manifest = {},
      testStore = new N3Store();

  // Parse the manifest into triples
  new N3Parser().parse(manifestContents, function (err, triple) {
    if (err)
      return callback(err);

    // Store triples until there are no more
    if (triple)
      return testStore.addTriple(triple.subject, triple.predicate, triple.object);

    // Once all triples are there, get the first item of the test list
    var tests = manifest.tests = [],
        itemHead = testStore.find('', prefixes.mf + 'entries', null)[0].object;
    // Loop through all test items
    while (itemHead && itemHead !== nil) {
      // Find and store the item's properties
      var itemValue = testStore.find(itemHead, first, null)[0].object,
          itemTriples = testStore.find(itemValue, null, null),
          test = { id: itemValue.replace(/^#/, '') };
      itemTriples.forEach(function (triple) {
        var propertyMatch = triple.predicate.match(/#(.+)/);
        if (propertyMatch)
          test[propertyMatch[1]] = triple.object;
      });
      tests.push(test);

      // Find the next test item
      itemHead = testStore.find(itemHead, rest, null)[0].object;
    }
    return callback(null, manifest);
  });
}

// Fetches and caches the specified file, or retrieves it from disk
function fetch(testFile, callback) {
  if (!testFile)
    return callback(null, null);

  var localFile = path.join(testFolder, testFile);
  fs.exists(localFile, function (exists) {
    if (exists)
      fs.readFile(localFile, 'utf8', callback);
    else
      request.get(testPath + testFile,
                  function (error, response, body) { callback(error, body); })
             .pipe(fs.createWriteStream(localFile));
  });
}

/*************************************************

  Individual test execution

**************************************************/

// Performs the specified test by parsing the specified Turtle document
function performTest(test, actionTurtle, callback) {
  // Create the results file
  var resultFile = path.join(outputFolder, test.action.replace(/\.ttl$/, '.nt')),
      resultStream = fs.createWriteStream(resultFile);

  resultStream.once('open', function () {
    // Try to parse the specified document
    var config = { documentURI: testPath + test.action };
    new N3Parser(config).parse(actionTurtle,
      function (error, triple) {
        if (error)
          test.error = error;

        // Write the triple to the results file, or end if none are left
        if (triple)
          resultStream.write(toNTriple(triple));
        else
          resultStream.end();
      });
  });

  // Verify the result if the result has been written
  resultStream.once('close', function () {
    verifyResult(test, resultFile, test.result && (testFolder + test.result), callback);
  });
}

// Verifies and reports the test result
function verifyResult(test, resultFile, correctFile, callback) {
  // Negative tests are successful if an error occurred
  var negativeTest = /TestTurtleNegative/.test(test.type);
  if (negativeTest) {
    displayResult(null, !!test.error);
  }
  // Positive tests are successful if the results are equal,
  // or if the correct solution is not given but no error occurred
  else {
    if (!correctFile)
      displayResult(null, !test.error);
    else if (resultFile)
      compareGraphs(resultFile, correctFile, displayResult);
    else
      displayResult(null, false);
  }

  // Display the test result
  function displayResult(error, success, comparison) {
    console.log(unString(test.name).bold + ':', unString(test.comment),
                (success ? 'OK'.green : 'FAIL'.red).bold);
    if (!success) {
      console.log((correctFile ? fs.readFileSync(correctFile, 'utf8') : '(empty)').grey);
      console.log('  was expected, but got'.bold.grey);
      console.log((resultFile ? fs.readFileSync(resultFile, 'utf8') : '(empty)').grey);
      console.log(('  error: '.bold + (test.error || '(none)')).grey);
      if (comparison)
        console.log(('  comparison: ' + comparison).grey);
    }
    test.success = success;
    callback(null, test);
  }
}

// Verifies whether the two graphs are equal
function compareGraphs(actual, expected, callback) {
  // Try a full-text comparison (fastest)
  async.parallel({
    actualContents: fs.readFile.bind(fs, actual, 'utf8'),
    expectedContents: fs.readFile.bind(fs, expected, 'utf8'),
  },
  function (error, results) {
    // If the full-text comparison was successful, graphs are certainly equal
    if (results.actualContents === results.expectedContents)
      callback(null, true);
    // If not, we check for proper graph equality with SWObjects
    else
      exec('sparql -d ' + expected + ' --compare ' + actual, function (error, stdout, stderr) {
        callback(error, /^matched\s*$/.test(stdout), stdout);
      });
  });
}

/*************************************************

  Conversion routines

**************************************************/

// Converts the triple to NTriples format (primitive and incomplete)
function toNTriple(triple) {
  var subject = triple.subject,
      predicate = triple.predicate,
      object = triple.object;

  if (/^".*"$/.test(object))
    object = escapeString(object);
  else
    object = escape(object);

  return (subject.match(/^_/)   ? subject   : '<' + subject   + '>') + ' ' +
         (predicate.match(/^_/) ? predicate : '<' + predicate + '>') + ' ' +
         (object.match(/^_|^"/) ? object    : '<' + object +    '>') + ' .\n';
}

// Removes the quotes around a string
function unString(value) {
  return value ? value.replace(/^("""|")(.*)\1$/, '$2') : '';
}

// Escapes unicode characters in a URI
function escape(value) {
  var result = '';

  // Add all characters, converting to an unicode escape code if necessary
  for (var i = 0; i < value.length; i++) {
    var code = value.charCodeAt(i);
    if (code >= 32 && code < 128) {
      result += value[i];
    }
    else {
      var hexCode = code.toString(16);
      while (hexCode.length < 4)
        hexCode = '0' + hexCode;
      result += '\\u' + hexCode;
    }
  }

  // Convert surrogate pairs to actual unicode (http://mathiasbynens.be/notes/javascript-encoding)
  result = result.replace(/\\u([a-z0-9]{4})\\u([a-z0-9]{4})/gi, function (all, high, low) {
    high = parseInt(high, 16);
    low = parseInt(low, 16);
    if (high >= 0xD800 && high <= 0xDBFF && low >= 0xDC00 && low <= 0xDFFF) {
      var result = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000;
      result = result.toString(16);
      while (result.length < 8)
        result = '0' + result;
      return '\\U' + result;
    }
    return all;
  });

  return result;
}

// Escapes characters in a string
function escapeString(value) {
  value = value.replace(/\\/g, '\\\\');
  value = escape(unString(value));
  value = value.replace(/"/g, '\\"');
  return '"' + value + '"';
}

/*************************************************

  EARL report generation

**************************************************/

var homepage = 'https://github.com/RubenVerborgh/N3.js',
    application = homepage + '#n3js',
    developer = 'http://ruben.verborgh.org/#me';

function generateEarlReport(tests, callback) {
  // Create the report file
  var reportFile = path.join(outputFolder, 'earl-report.ttl'),
      report = fs.createWriteStream(reportFile),
      date = new Date().toISOString();

  report.once('open', function () {
    for (var prefix in prefixes)
      writeln('@prefix ', prefix, ': <', prefixes[prefix], '>.');
    writeln();

    writeln('<> foaf:primaryTopic <', application, '>;');
    writeln('  dc:issued "', date, '"^^xsd:dateTime;');
    writeln('  foaf:maker <', developer, '>.');
    writeln();

    writeln('<', application, '> a earl:Software, earl:TestSubject, doap:Project;');
    writeln('  doap:name "N3.js";');
    writeln('  doap:homepage <', homepage, '>;');
    writeln('  doap:license <http://opensource.org/licenses/MIT>;');
    writeln('  doap:programming-language "JavaScript";');
    writeln('  doap:implements <http://www.w3.org/TR/turtle/>;');
    writeln('  doap:category <http://dbpedia.org/resource/Resource_Description_Framework>;');
    writeln('  doap:download-page <https://npmjs.org/package/n3>;');
    writeln('  doap:bug-database <', homepage, '/issues>;');
    writeln('  doap:blog <http://ruben.verborgh.org/blog/>;');
    writeln('  doap:developer <', developer, '>;');
    writeln('  doap:maintainer <', developer, '>;');
    writeln('  doap:documenter <', developer, '>;');
    writeln('  doap:maker <', developer, '>;');
    writeln('  dc:title "N3.js";');
    writeln('  dc:description   "N3.js is an asynchronous, streaming Turtle parser for JavaScript."@en;');
    writeln('  doap:description "N3.js is an asynchronous, streaming Turtle parser for JavaScript."@en;');
    writeln('  dc:creator <', developer, '>.');
    writeln();

    writeln('<', developer, '> a foaf:Person, earl:Assertor;');
    writeln('  foaf:name "Ruben Verborgh";');
    writeln('  foaf:homepage <http://ruben.verborgh.org/>;');
    writeln('  foaf:primaryTopicOf <http://ruben.verborgh.org/profile/>;');
    writeln('  rdfs:isDefinedBy <http://ruben.verborgh.org/profile/>.');

    tests.forEach(function (test) {
      writeln();
      writeln('manifest:', test.id, ' a earl:TestCriterion, earl:TestCase;');
      writeln('  dc:title ', escapeString(unString(test.name)), ';');
      writeln('  dc:description ', escapeString(unString(test.comment)), ';');
      writeln('  mf:action <', testPath, test.action, '>;');
      if (test.result)
        writeln('  mf:result <', testPath, test.result, '>;');
      writeln('  earl:assertions (');
      writeln('     [ a earl:Assertion;');
      writeln('       earl:assertedBy <', developer, '>;');
      writeln('       earl:test manifest:', test.id, ';');
      writeln('       earl:subject <', application, '>;');
      writeln('       earl:mode earl:automatic;');
      writeln('       earl:result [ a earl:TestResult; ',
                        'earl:outcome earl:', (test.success ? 'passed' : 'failed'), '; ',
                        'dc:date "', date, '"^^xsd:dateTime',
                      ' ]]');
      writeln('  ).');
    });

    report.end();
  });

  report.once('close', callback);

  function writeln() {
    for(var i = 0; i < arguments.length; i++)
      report.write(arguments[i]);
    report.write('\n');
  }
}
<|endoftext|>


<commit_before>/* eslint-env mocha */
import expect from 'expect';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-dom/test-utils';
import Modal from '../src/components/Modal';
import * as ariaAppHider from '../src/helpers/ariaAppHider';
import {
  isBodyWithReactModalOpenClass,
  contentAttribute,
  mcontent, moverlay,
  escKeyDown,
  renderModal, unmountModal, emptyDOM
} from './helper';

describe('State', () => {
  afterEach('check if test cleaned up rendered modals', emptyDOM);

  it('scopes tab navigation to the modal');
  it('focuses the last focused element when tabbing in from browser chrome');
  it('renders children [tested indirectly]');

  it('can be open initially', () => {
    const modal = renderModal({ isOpen: true }, 'hello');
    expect(mcontent(modal)).toExist();
  });

  it('can be closed initially', () => {
    const modal = renderModal({}, 'hello');
    expect(ReactDOM.findDOMNode(mcontent(modal))).toNotExist();
  });

  it('doesn\'t render the portal if modal is closed', () => {
    const modal = renderModal({}, 'hello');
    expect(ReactDOM.findDOMNode(modal.portal)).toNotExist();
  })

  it('has default props', () => {
    const node = document.createElement('div');
    Modal.setAppElement(document.createElement('div'));
    const modal = ReactDOM.render(<Modal />, node);
    const props = modal.props;
    expect(props.isOpen).toBe(false);
    expect(props.ariaHideApp).toBe(true);
    expect(props.closeTimeoutMS).toBe(0);
    expect(props.shouldCloseOnOverlayClick).toBe(true);
    ReactDOM.unmountComponentAtNode(node);
    ariaAppHider.resetForTesting();
    Modal.setAppElement(document.body);  // restore default
  });

  it('accepts appElement as a prop', () => {
    const el = document.createElement('div');
    const node = document.createElement('div');
    ReactDOM.render((
      <Modal isOpen={true} appElement={el} />
    ), node);
    expect(el.getAttribute('aria-hidden')).toEqual('true');
    ReactDOM.unmountComponentAtNode(node);
  });

  it('renders into the body, not in context', () => {
    const node = document.createElement('div');
    class App extends Component {
      render() {
        return (

            <Modal isOpen>
              hello
            </Modal>

        );
      }
    }
    Modal.setAppElement(node);
    ReactDOM.render(<App />, node);
    expect(
      document.body.querySelector('.ReactModalPortal').parentNode
    ).toEqual(
      document.body
    );
    ReactDOM.unmountComponentAtNode(node);
  });

  it('renders the modal content with a dialog aria role when provided ', () => {
    const child = 'I am a child of Modal, and he has sent me here...';
    const modal = renderModal({ isOpen: true, role: 'dialog' }, child);
    expect(contentAttribute(modal, 'role')).toEqual('dialog');
  });

  it('set aria-label based on the contentLabel prop', () => {
    const child = 'I am a child of Modal, and he has sent me here...';
    const modal = renderModal({
      isOpen: true,
      contentLabel: 'Special Modal'
    }, child);
    expect(
      contentAttribute(modal, 'aria-label')
    ).toEqual('Special Modal');
  });

  it('removes the portal node', () => {
    const modal = renderModal({ isOpen: true }, 'hello');
    unmountModal();
    expect(document.querySelector('.ReactModalPortal')).toNotExist();
  });

  it('removes the portal node after closeTimeoutMS', done => {
    const closeTimeoutMS = 100;
    renderModal({ isOpen: true, closeTimeoutMS }, 'hello');

    function checkDOM(count) {
      const portal = document.querySelectorAll('.ReactModalPortal');
      expect(portal.length).toBe(count);
    }

    unmountModal();

    // content is still mounted after modal is gone
    checkDOM(1);

    setTimeout(() => {
      // content is unmounted after specified timeout
      checkDOM(0);
      done();
    }, closeTimeoutMS);
  });

  it('focuses the modal content', () => {
    const modal = renderModal({ isOpen: true }, null);
    expect(document.activeElement).toBe(mcontent(modal));
  });

  it('give back focus to previous element or modal.', done => {
    function cleanup () {
      unmountModal();
      done();
    }
    const modalA = renderModal({
      isOpen: true,
      className: 'modal-a',
      onRequestClose: cleanup
    }, null);

    const modalContent = mcontent(modalA);
    expect(document.activeElement).toEqual(modalContent);

    const modalB = renderModal({
      isOpen: true,
      className: 'modal-b',
      onRequestClose() {
        const modalContent = mcontent(modalB);
        expect(document.activeElement).toEqual(mcontent(modalA));
        escKeyDown(modalContent);
        expect(document.activeElement).toEqual(modalContent);
      }
    }, null);

    escKeyDown(modalContent);
  });

  it('does not steel focus when a descendent is already focused', () => {
    let content;
    const input = (
      <input ref={(el) => { el && el.focus(); content = el; }} />
    );
    renderModal({ isOpen: true }, input, () => {
      expect(document.activeElement).toEqual(content);
    });
  });

  it('supports portalClassName', () => {
    const modal = renderModal({
      isOpen: true,
      portalClassName: 'myPortalClass'
    });
    expect(modal.node.className.includes('myPortalClass')).toBeTruthy();
  });

  it('supports custom className', () => {
    const modal = renderModal({ isOpen: true, className: 'myClass' });
    expect(
      mcontent(modal).className.includes('myClass')
    ).toBeTruthy();
  });

  it('supports overlayClassName', () => {
    const modal = renderModal({
      isOpen: true,
      overlayClassName: 'myOverlayClass'
    });
    expect(
      moverlay(modal).className.includes('myOverlayClass')
    ).toBeTruthy();
  });

  it('overrides content classes with custom object className', () => {
    const modal = renderModal({
      isOpen: true,
      className: {
        base: 'myClass',
        afterOpen: 'myClass_after-open',
        beforeClose: 'myClass_before-close'
      }
    });
    expect(
      mcontent(modal).className
    ).toEqual(
      'myClass myClass_after-open'
    );
    unmountModal();
  });

  it('overrides overlay classes with custom object overlayClassName', () => {
    const modal = renderModal({
      isOpen: true,
      overlayClassName: {
        base: 'myOverlayClass',
        afterOpen: 'myOverlayClass_after-open',
        beforeClose: 'myOverlayClass_before-close'
      }
    });
    expect(
      moverlay(modal).className
    ).toEqual(
      'myOverlayClass myOverlayClass_after-open'
    );
    unmountModal();
  });

  it('supports overriding react modal open class in document.body.', () => {
    const modal = renderModal({
      isOpen: true,
      bodyOpenClassName: 'custom-modal-open'
    });
    expect(
      document.body.className.indexOf('custom-modal-open') > -1
    ).toBeTruthy();
  });

  it('don\'t append class to document.body if modal is not open', () => {
    renderModal({ isOpen: false });
    expect(!isBodyWithReactModalOpenClass()).toBeTruthy();
    unmountModal();
  });

  it('append class to document.body if modal is open', () => {
    renderModal({ isOpen: true });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    unmountModal();
  });

  it('removes class from document.body when unmounted without closing', () => {
    renderModal({ isOpen: true });
    unmountModal();
    expect(!isBodyWithReactModalOpenClass()).toBeTruthy();
  });

  it('remove class from document.body when no modals opened', () => {
    renderModal({ isOpen: true });
    renderModal({ isOpen: true });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    unmountModal();
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    unmountModal();
    expect(!isBodyWithReactModalOpenClass()).toBeTruthy();
  });

  it('should not add classes to document.body for unopened modals', () => {
    renderModal({ isOpen: true });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    renderModal({ isOpen: false, bodyOpenClassName: 'testBodyClass' });
    expect(!isBodyWithReactModalOpenClass('testBodyClass')).toBeTruthy()
  });

  it('should not remove classes from document.body when rendering unopened modal', () => {
    renderModal({ isOpen: true });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    renderModal({ isOpen: false, bodyOpenClassName: 'testBodyClass' });
    renderModal({ isOpen: false });
    expect(!isBodyWithReactModalOpenClass('testBodyClass')).toBeTruthy()
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    renderModal({ isOpen: false });
    renderModal({ isOpen: false });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
  });

  it('additional aria attributes', () => {
    const modal = renderModal({ isOpen: true, aria: { labelledby: "a" }}, 'hello');
    expect(
      mcontent(modal).getAttribute('aria-labelledby')
    ).toEqual("a");
    unmountModal();
  });

  it('adding/removing aria-hidden without an appElement will try to fallback to document.body', () => {
    ariaAppHider.documentNotReadyOrSSRTesting();
    const node = document.createElement('div');
    ReactDOM.render((
      <Modal isOpen />
    ), node);
    expect(document.body.getAttribute('aria-hidden')).toEqual('true');
    ReactDOM.unmountComponentAtNode(node);
    expect(document.body.getAttribute('aria-hidden')).toEqual(null);
  });

  it('raise an exception if appElement is a selector and no elements were found.', () => {
    expect(() => ariaAppHider.setElement('.test')).toThrow();
  });

  it('removes aria-hidden from appElement when unmounted w/o closing', () => {
    const el = document.createElement('div');
    const node = document.createElement('div');
    ReactDOM.render((
      <Modal isOpen appElement={el} />
    ), node);
    expect(el.getAttribute('aria-hidden')).toEqual('true');
    ReactDOM.unmountComponentAtNode(node);
    expect(el.getAttribute('aria-hidden')).toEqual(null);
  });

  it('adds --after-open for animations', () => {
    const modal = renderModal({ isOpen: true });
    const rg = /--after-open/i;
    expect(rg.test(mcontent(modal).className)).toBeTruthy();
    expect(rg.test(moverlay(modal).className)).toBeTruthy();
  });

  it('adds --before-close for animations', () => {
    const closeTimeoutMS = 50;
    const modal = renderModal({
      isOpen: true,
      closeTimeoutMS
    });
    modal.portal.closeWithTimeout();

    const rg = /--before-close/i;
    expect(rg.test(moverlay(modal).className)).toBeTruthy();
    expect(rg.test(mcontent(modal).className)).toBeTruthy();

    modal.portal.closeWithoutTimeout();
  });

  it('should not be open after close with time out and reopen it', () => {
    const modal = renderModal({
      isOpen: true,
      closeTimeoutMS: 2000,
      onRequestClose() {}
    });
    modal.portal.closeWithTimeout();
    modal.portal.open();
    modal.portal.closeWithoutTimeout();
    expect(!modal.portal.state.isOpen).toBeTruthy();
  });

  it('verify default prop of shouldCloseOnOverlayClick', () => {
    const modal = renderModal({ isOpen: true });
    expect(modal.props.shouldCloseOnOverlayClick).toBeTruthy();
  });

  it('verify prop of shouldCloseOnOverlayClick', () => {
    const modalOpts = { isOpen: true, shouldCloseOnOverlayClick: false };
    const modal = renderModal(modalOpts);
    expect(!modal.props.shouldCloseOnOverlayClick).toBeTruthy();
  });

  it('keeps the modal in the DOM until closeTimeoutMS elapses', done => {
    const closeTimeoutMS = 100;

    const modal = renderModal({ isOpen: true, closeTimeoutMS });
    modal.portal.closeWithTimeout();

    function checkDOM(count) {
      const overlay = document.querySelectorAll('.ReactModal__Overlay');
      const content = document.querySelectorAll('.ReactModal__Content');
      expect(overlay.length).toBe(count);
      expect(content.length).toBe(count);
    }

    // content is still mounted after modal is gone
    checkDOM(1);

    setTimeout(() => {
      // content is unmounted after specified timeout
      checkDOM(0);
      done();
    }, closeTimeoutMS);
  });

  it('shouldn\'t throw if forcibly unmounted during mounting', () => {
    /* eslint-disable camelcase, react/prop-types */
    class Wrapper extends Component {
      constructor (props) {
        super(props);
        this.state = { error: false };
      }
      unstable_handleError () {
        this.setState({ error: true });
      }
      render () {
        return this.state.error ? null :
{ this.props.children }
;
      }
    }
    /* eslint-enable camelcase, react/prop-types */

    const Throw = () => { throw new Error('reason'); };
    const TestCase = () => (
      <Wrapper>
        <Modal />
        <Throw />
      </Wrapper>
    );

    const currentDiv = document.createElement('div');
    document.body.appendChild(currentDiv);

    const mount = () => ReactDOM.render(<TestCase />, currentDiv);
    expect(mount).toNotThrow();

    document.body.removeChild(currentDiv);
  });

  it('verify that portalClassName is refreshed on component update', () => {
    const node = document.createElement('div');
    let modal = null;

    class App extends Component {
      constructor(props) {
        super(props);
        this.state = { testHasChanged: false };
      }

      componentDidMount() {
        expect(modal.node.className).toEqual('myPortalClass');

        this.setState({
          testHasChanged: true
        });
      }

      componentDidUpdate() {
        expect(modal.node.className).toEqual('myPortalClass-modifier');
      }

      render() {
        const portalClassName = this.state.testHasChanged === true ?
          'myPortalClass-modifier' : 'myPortalClass';

        return (

            <Modal
              ref={modalComponent => { modal = modalComponent; }}
              isOpen
              portalClassName={portalClassName}>
              Test
            </Modal>

        );
      }
    }

    Modal.setAppElement(node);
    ReactDOM.render(<App />, node);
  });
});
<commit_msg>[chore] added test for default parentSelector.<commit_after>/* eslint-env mocha */
import expect from 'expect';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-dom/test-utils';
import Modal from '../src/components/Modal';
import * as ariaAppHider from '../src/helpers/ariaAppHider';
import {
  isBodyWithReactModalOpenClass,
  contentAttribute,
  mcontent, moverlay,
  escKeyDown,
  renderModal, unmountModal, emptyDOM
} from './helper';

describe('State', () => {
  afterEach('check if test cleaned up rendered modals', emptyDOM);

  it('scopes tab navigation to the modal');
  it('focuses the last focused element when tabbing in from browser chrome');
  it('renders children [tested indirectly]');

  it('can be open initially', () => {
    const modal = renderModal({ isOpen: true }, 'hello');
    expect(mcontent(modal)).toExist();
  });

  it('can be closed initially', () => {
    const modal = renderModal({}, 'hello');
    expect(ReactDOM.findDOMNode(mcontent(modal))).toNotExist();
  });

  it('doesn\'t render the portal if modal is closed', () => {
    const modal = renderModal({}, 'hello');
    expect(ReactDOM.findDOMNode(modal.portal)).toNotExist();
  })

  it('has default props', () => {
    const node = document.createElement('div');
    Modal.setAppElement(document.createElement('div'));
    const modal = ReactDOM.render(<Modal />, node);
    const props = modal.props;
    expect(props.isOpen).toBe(false);
    expect(props.ariaHideApp).toBe(true);
    expect(props.closeTimeoutMS).toBe(0);
    expect(props.shouldCloseOnOverlayClick).toBe(true);
    ReactDOM.unmountComponentAtNode(node);
    ariaAppHider.resetForTesting();
    Modal.setAppElement(document.body);  // restore default
  });

  it('accepts appElement as a prop', () => {
    const el = document.createElement('div');
    const node = document.createElement('div');
    ReactDOM.render((
      <Modal isOpen={true} appElement={el} />
    ), node);
    expect(el.getAttribute('aria-hidden')).toEqual('true');
    ReactDOM.unmountComponentAtNode(node);
  });

  it('renders into the body, not in context', () => {
    const node = document.createElement('div');
    class App extends Component {
      render() {
        return (

            <Modal isOpen>
              hello
            </Modal>

        );
      }
    }
    Modal.setAppElement(node);
    ReactDOM.render(<App />, node);
    expect(
      document.body.querySelector('.ReactModalPortal').parentNode
    ).toEqual(
      document.body
    );
    ReactDOM.unmountComponentAtNode(node);
  });

  it ('default parentSelector should be document.body.', () => {
    const modal = renderModal({ isOpen: true });
    expect(modal.props.parentSelector()).toEqual(document.body);
  });

  it('renders the modal content with a dialog aria role when provided ', () => {
    const child = 'I am a child of Modal, and he has sent me here...';
    const modal = renderModal({ isOpen: true, role: 'dialog' }, child);
    expect(contentAttribute(modal, 'role')).toEqual('dialog');
  });

  it('set aria-label based on the contentLabel prop', () => {
    const child = 'I am a child of Modal, and he has sent me here...';
    const modal = renderModal({
      isOpen: true,
      contentLabel: 'Special Modal'
    }, child);
    expect(
      contentAttribute(modal, 'aria-label')
    ).toEqual('Special Modal');
  });

  it('removes the portal node', () => {
    const modal = renderModal({ isOpen: true }, 'hello');
    unmountModal();
    expect(document.querySelector('.ReactModalPortal')).toNotExist();
  });

  it('removes the portal node after closeTimeoutMS', done => {
    const closeTimeoutMS = 100;
    renderModal({ isOpen: true, closeTimeoutMS }, 'hello');

    function checkDOM(count) {
      const portal = document.querySelectorAll('.ReactModalPortal');
      expect(portal.length).toBe(count);
    }

    unmountModal();

    // content is still mounted after modal is gone
    checkDOM(1);

    setTimeout(() => {
      // content is unmounted after specified timeout
      checkDOM(0);
      done();
    }, closeTimeoutMS);
  });

  it('focuses the modal content', () => {
    const modal = renderModal({ isOpen: true }, null);
    expect(document.activeElement).toBe(mcontent(modal));
  });

  it('give back focus to previous element or modal.', done => {
    function cleanup () {
      unmountModal();
      done();
    }
    const modalA = renderModal({
      isOpen: true,
      className: 'modal-a',
      onRequestClose: cleanup
    }, null);

    const modalContent = mcontent(modalA);
    expect(document.activeElement).toEqual(modalContent);

    const modalB = renderModal({
      isOpen: true,
      className: 'modal-b',
      onRequestClose() {
        const modalContent = mcontent(modalB);
        expect(document.activeElement).toEqual(mcontent(modalA));
        escKeyDown(modalContent);
        expect(document.activeElement).toEqual(modalContent);
      }
    }, null);

    escKeyDown(modalContent);
  });

  it('does not steel focus when a descendent is already focused', () => {
    let content;
    const input = (
      <input ref={(el) => { el && el.focus(); content = el; }} />
    );
    renderModal({ isOpen: true }, input, () => {
      expect(document.activeElement).toEqual(content);
    });
  });

  it('supports portalClassName', () => {
    const modal = renderModal({
      isOpen: true,
      portalClassName: 'myPortalClass'
    });
    expect(modal.node.className.includes('myPortalClass')).toBeTruthy();
  });

  it('supports custom className', () => {
    const modal = renderModal({ isOpen: true, className: 'myClass' });
    expect(
      mcontent(modal).className.includes('myClass')
    ).toBeTruthy();
  });

  it('supports overlayClassName', () => {
    const modal = renderModal({
      isOpen: true,
      overlayClassName: 'myOverlayClass'
    });
    expect(
      moverlay(modal).className.includes('myOverlayClass')
    ).toBeTruthy();
  });

  it('overrides content classes with custom object className', () => {
    const modal = renderModal({
      isOpen: true,
      className: {
        base: 'myClass',
        afterOpen: 'myClass_after-open',
        beforeClose: 'myClass_before-close'
      }
    });
    expect(
      mcontent(modal).className
    ).toEqual(
      'myClass myClass_after-open'
    );
    unmountModal();
  });

  it('overrides overlay classes with custom object overlayClassName', () => {
    const modal = renderModal({
      isOpen: true,
      overlayClassName: {
        base: 'myOverlayClass',
        afterOpen: 'myOverlayClass_after-open',
        beforeClose: 'myOverlayClass_before-close'
      }
    });
    expect(
      moverlay(modal).className
    ).toEqual(
      'myOverlayClass myOverlayClass_after-open'
    );
    unmountModal();
  });

  it('supports overriding react modal open class in document.body.', () => {
    const modal = renderModal({
      isOpen: true,
      bodyOpenClassName: 'custom-modal-open'
    });
    expect(
      document.body.className.indexOf('custom-modal-open') > -1
    ).toBeTruthy();
  });

  it('don\'t append class to document.body if modal is not open', () => {
    renderModal({ isOpen: false });
    expect(!isBodyWithReactModalOpenClass()).toBeTruthy();
    unmountModal();
  });

  it('append class to document.body if modal is open', () => {
    renderModal({ isOpen: true });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    unmountModal();
  });

  it('removes class from document.body when unmounted without closing', () => {
    renderModal({ isOpen: true });
    unmountModal();
    expect(!isBodyWithReactModalOpenClass()).toBeTruthy();
  });

  it('remove class from document.body when no modals opened', () => {
    renderModal({ isOpen: true });
    renderModal({ isOpen: true });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    unmountModal();
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    unmountModal();
    expect(!isBodyWithReactModalOpenClass()).toBeTruthy();
  });

  it('should not add classes to document.body for unopened modals', () => {
    renderModal({ isOpen: true });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    renderModal({ isOpen: false, bodyOpenClassName: 'testBodyClass' });
    expect(!isBodyWithReactModalOpenClass('testBodyClass')).toBeTruthy()
  });

  it('should not remove classes from document.body when rendering unopened modal', () => {
    renderModal({ isOpen: true });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    renderModal({ isOpen: false, bodyOpenClassName: 'testBodyClass' });
    renderModal({ isOpen: false });
    expect(!isBodyWithReactModalOpenClass('testBodyClass')).toBeTruthy()
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
    renderModal({ isOpen: false });
    renderModal({ isOpen: false });
    expect(isBodyWithReactModalOpenClass()).toBeTruthy();
  });

  it('additional aria attributes', () => {
    const modal = renderModal({ isOpen: true, aria: { labelledby: "a" }}, 'hello');
    expect(
      mcontent(modal).getAttribute('aria-labelledby')
    ).toEqual("a");
    unmountModal();
  });

  it('adding/removing aria-hidden without an appElement will try to fallback to document.body', () => {
    ariaAppHider.documentNotReadyOrSSRTesting();
    const node = document.createElement('div');
    ReactDOM.render((
      <Modal isOpen />
    ), node);
    expect(document.body.getAttribute('aria-hidden')).toEqual('true');
    ReactDOM.unmountComponentAtNode(node);
    expect(document.body.getAttribute('aria-hidden')).toEqual(null);
  });

  it('raise an exception if appElement is a selector and no elements were found.', () => {
    expect(() => ariaAppHider.setElement('.test')).toThrow();
  });

  it('removes aria-hidden from appElement when unmounted w/o closing', () => {
    const el = document.createElement('div');
    const node = document.createElement('div');
    ReactDOM.render((
      <Modal isOpen appElement={el} />
    ), node);
    expect(el.getAttribute('aria-hidden')).toEqual('true');
    ReactDOM.unmountComponentAtNode(node);
    expect(el.getAttribute('aria-hidden')).toEqual(null);
  });

  it('adds --after-open for animations', () => {
    const modal = renderModal({ isOpen: true });
    const rg = /--after-open/i;
    expect(rg.test(mcontent(modal).className)).toBeTruthy();
    expect(rg.test(moverlay(modal).className)).toBeTruthy();
  });

  it('adds --before-close for animations', () => {
    const closeTimeoutMS = 50;
    const modal = renderModal({
      isOpen: true,
      closeTimeoutMS
    });
    modal.portal.closeWithTimeout();

    const rg = /--before-close/i;
    expect(rg.test(moverlay(modal).className)).toBeTruthy();
    expect(rg.test(mcontent(modal).className)).toBeTruthy();

    modal.portal.closeWithoutTimeout();
  });

  it('should not be open after close with time out and reopen it', () => {
    const modal = renderModal({
      isOpen: true,
      closeTimeoutMS: 2000,
      onRequestClose() {}
    });
    modal.portal.closeWithTimeout();
    modal.portal.open();
    modal.portal.closeWithoutTimeout();
    expect(!modal.portal.state.isOpen).toBeTruthy();
  });

  it('verify default prop of shouldCloseOnOverlayClick', () => {
    const modal = renderModal({ isOpen: true });
    expect(modal.props.shouldCloseOnOverlayClick).toBeTruthy();
  });

  it('verify prop of shouldCloseOnOverlayClick', () => {
    const modalOpts = { isOpen: true, shouldCloseOnOverlayClick: false };
    const modal = renderModal(modalOpts);
    expect(!modal.props.shouldCloseOnOverlayClick).toBeTruthy();
  });

  it('keeps the modal in the DOM until closeTimeoutMS elapses', done => {
    const closeTimeoutMS = 100;

    const modal = renderModal({ isOpen: true, closeTimeoutMS });
    modal.portal.closeWithTimeout();

    function checkDOM(count) {
      const overlay = document.querySelectorAll('.ReactModal__Overlay');
      const content = document.querySelectorAll('.ReactModal__Content');
      expect(overlay.length).toBe(count);
      expect(content.length).toBe(count);
    }

    // content is still mounted after modal is gone
    checkDOM(1);

    setTimeout(() => {
      // content is unmounted after specified timeout
      checkDOM(0);
      done();
    }, closeTimeoutMS);
  });

  it('shouldn\'t throw if forcibly unmounted during mounting', () => {
    /* eslint-disable camelcase, react/prop-types */
    class Wrapper extends Component {
      constructor (props) {
        super(props);
        this.state = { error: false };
      }
      unstable_handleError () {
        this.setState({ error: true });
      }
      render () {
        return this.state.error ? null :
{ this.props.children }
;
      }
    }
    /* eslint-enable camelcase, react/prop-types */

    const Throw = () => { throw new Error('reason'); };
    const TestCase = () => (
      <Wrapper>
        <Modal />
        <Throw />
      </Wrapper>
    );

    const currentDiv = document.createElement('div');
    document.body.appendChild(currentDiv);

    const mount = () => ReactDOM.render(<TestCase />, currentDiv);
    expect(mount).toNotThrow();

    document.body.removeChild(currentDiv);
  });

  it('verify that portalClassName is refreshed on component update', () => {
    const node = document.createElement('div');
    let modal = null;

    class App extends Component {
      constructor(props) {
        super(props);
        this.state = { testHasChanged: false };
      }

      componentDidMount() {
        expect(modal.node.className).toEqual('myPortalClass');

        this.setState({
          testHasChanged: true
        });
      }

      componentDidUpdate() {
        expect(modal.node.className).toEqual('myPortalClass-modifier');
      }

      render() {
        const portalClassName = this.state.testHasChanged === true ?
          'myPortalClass-modifier' : 'myPortalClass';

        return (

            <Modal
              ref={modalComponent => { modal = modalComponent; }}
              isOpen
              portalClassName={portalClassName}>
              Test
            </Modal>

        );
      }
    }

    Modal.setAppElement(node);
    ReactDOM.render(<App />, node);
  });
});
<|endoftext|>


<commit_before>(function() {

  "use strict";

  var math = require('mathjs');

  // var R = require('ramda');
  var Rp = require('./plus-fp/plus-fp');

  var rotate = function(point, centre, angle) {

    // assumes zero centre

    var x = point.x - centre.x;
    var y = point.y - centre.y;

    if ((x !== 0) || (y !== 0)) {
      var r = math.sqrt(x * x + y * y);
      var theta = math.atan2(y, x);

      theta += angle;

      x = r * math.cos(theta);
      y = r * math.sin(theta);

      x += centre.x;
      y += centre.y;
    }

    return {
      x: x,
      y: y
    };
  };

  var makeLine = function(x1, y1, x2, y2, centre, angle) {

    let point1 = {
      x: x1,
      y: y1
    };

    let point2 = {
      x: x2,
      y: y2
    };

    point1 = rotate(point1, centre, angle);
    point2 = rotate(point2, centre, angle);

    return {
      p1: point1,
      p2: point2
    };
  };

  var makePolygon = () => {

    let polygon = {};

    var centre = {
      x: 0,
      y: 0
    };

    var points = 3 + Rp.bandomInt(6, 2);
    var radius1 = math.random(0, 1);
    var radius2 = radius1; //math.random(0, 1);
    var radius = math.max(radius1, radius2);
    var angle = math.random(0, 2 * math.pi);

    var lines = [];

    for (var p = 0; p < points; p++) {
      var x1 = centre.x + radius1 * math.cos(math.pi * 2 / points * p);
      var y1 = centre.y + radius2 * math.sin(math.pi * 2 / points * p);

      var x2 = centre.x + radius1 * math.cos(math.pi * 2 / points * (p + 1));
      var y2 = centre.y + radius2 * math.sin(math.pi * 2 / points * (p + 1));

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));
    }

    polygon.lines = lines;

    polygon.params = {
      radius: radius,
      angle: angle
    };

    polygon.name = "polygon";

    return polygon;
  };

  var makeCrosshatch = function() {
    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let points1 = 2 + Rp.bandomInt(6, 2);
    let points2 = 2 + Rp.bandomInt(6, 2);
    let angle = math.random(0, 2 * math.pi);
    let isBordered = math.random(0, 1) < 0.5;

    let lines = [];

    for (let p1 = (isBordered ? 0 : 1); p1 < (isBordered ? points1 + 1 : points1); p1++) {
      let x1 = radius * (-1 + 2 / points1 * p1);
      let y1 = -radius;

      let x2 = x1;
      let y2 = radius;

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));
    }

    for (let p2 = (isBordered ? 0 : 1); p2 < (isBordered ? points2 + 1 : points2); p2++) {
      let x1 = -radius;
      let y1 = radius * (-1 + 2 / points2 * p2);

      let x2 = radius;
      let y2 = y1;

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));
    }

    shape.lines = lines;

    shape.params = {
      points1: points1,
      points2: points2,
      isBordered: isBordered,
      angle: angle
    };

    shape.name = "crosshatch";

    return shape;
  };

  var makeZigzag = function() {
    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let points = 3 + Rp.bandomInt(8, 2);
    let angle = math.random(0, 2 * math.pi);

    let lines = [];

    for (let p = 0; p < points; p++) {

      let x1 = radius * (p % 2 ? 1 : -1);
      let x2 = -x1;

      let y1 = radius - (2 * radius) / points * p;
      let y2 = radius - (2 * radius) / points * (p + 1);

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));
    }

    shape.lines = lines;

    shape.params = {
      points: points,
      angle: angle
    };

    shape.name = "zigzag";

    return shape;
  };

  var makeWavy = function() {
    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let points = 50; // segments
    let angle = math.random(0, 2 * math.pi);

    let freq = math.randomInt(1, 9);
    let ampl = radius;
    let phase = math.random(0, 2 * math.pi);

    let lines = [];

    var pif = 2 * math.pi * freq / radius  / 2;

    for (let p = 0; p < points; p++) {

      let x1 = -radius + (2 * radius) / points * p;
      let x2 = -radius + (2 * radius) / points * (p + 1);

      let y1 = ampl * math.sin( pif * x1 + phase);
      let y2 = ampl * math.sin( pif * x2 + phase);

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));
    }

    shape.lines = lines;

    shape.params = {
      freq: freq,
      ampl: ampl,
      phase: phase,
      radius: radius,
      angle: angle
    };

    shape.name = "wavy";

    return shape;
  };

  var makeAsterisk = function() {

    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let linesCount = 2 + Rp.bandomInt(7, 1);
    let angle = math.random(0, 2 * math.pi);

    let lines = [];

    for (let p1 = 0; p1 < linesCount; p1++) {
      lines.push(makeLine(-radius, 0, radius, 0, centre, math.pi * 2 / linesCount * p1 + angle));
    }

    shape.lines = lines;

    shape.params = {
      angle: angle
    };

    shape.name = "asterisk";

    return shape;
  };

  var makeArrow = function() {

    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let angle = math.random(0, 2 * math.pi);

    let lines = [];

    // stem
    lines.push(makeLine(-radius, 0, radius, 0, centre, angle));

    // arrow
    lines.push(makeLine(radius / 2, radius / 2, radius, 0, centre, angle));
    lines.push(makeLine(radius / 2, -radius / 2, radius, 0, centre, angle));

    shape.lines = lines;

    shape.params = {
      angle: angle
    };

    shape.name = "arrow";

    return shape;
  };

  //---

  exports.shapers = [
    {
      fn: function() {

        return {
          name: "polygon",
          make: () => {

            return makePolygon();
          }
        };
      },
      weight: 4,
    },
    {
      fn: function() {

        return {
          name: "crosshatch",
          make: () => {

            return makeCrosshatch();
          }
        };
      },
      weight: 1,
    },
    {
      fn: function() {

        return {
          name: "asterisk",
          make: () => {

            return makeAsterisk();
          }
        };
      },
      weight: 1,
    },
    {
      fn: function() {

        return {
          name: "arrow",
          make: () => {

            return makeArrow();
          }
        };
      },
      weight: 1,
    },
    {
      fn: function() {

        return {
          name: "zigzag",
          make: () => {

            return makeZigzag();
          }
        };
      },
      weight: 1
    },
    {
      fn: function() {

        return {
          name: "wavy",
          make: () => {

            return makeWavy();
          }
        };
      },
      weight: 4
    }
  ];
}());
<commit_msg>helix<commit_after>(function() {

  "use strict";

  var math = require('mathjs');

  // var R = require('ramda');
  var Rp = require('./plus-fp/plus-fp');

  var rotate = function(point, centre, angle) {

    // assumes zero centre

    var x = point.x - centre.x;
    var y = point.y - centre.y;

    if ((x !== 0) || (y !== 0)) {
      var r = math.sqrt(x * x + y * y);
      var theta = math.atan2(y, x);

      theta += angle;

      x = r * math.cos(theta);
      y = r * math.sin(theta);

      x += centre.x;
      y += centre.y;
    }

    return {
      x: x,
      y: y
    };
  };

  var makeLine = function(x1, y1, x2, y2, centre, angle) {

    let point1 = {
      x: x1,
      y: y1
    };

    let point2 = {
      x: x2,
      y: y2
    };

    point1 = rotate(point1, centre, angle);
    point2 = rotate(point2, centre, angle);

    return {
      p1: point1,
      p2: point2
    };
  };

  var makePolygon = () => {

    let polygon = {};

    var centre = {
      x: 0,
      y: 0
    };

    var points = 3 + Rp.bandomInt(6, 2);
    var radius1 = math.random(0, 1);
    var radius2 = radius1; //math.random(0, 1);
    var radius = math.max(radius1, radius2);
    var angle = math.random(0, 2 * math.pi);

    var lines = [];

    for (var p = 0; p < points; p++) {
      var x1 = centre.x + radius1 * math.cos(math.pi * 2 / points * p);
      var y1 = centre.y + radius2 * math.sin(math.pi * 2 / points * p);

      var x2 = centre.x + radius1 * math.cos(math.pi * 2 / points * (p + 1));
      var y2 = centre.y + radius2 * math.sin(math.pi * 2 / points * (p + 1));

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));
    }

    polygon.lines = lines;

    polygon.params = {
      radius: radius,
      angle: angle
    };

    polygon.name = "polygon";

    return polygon;
  };

  var makeCrosshatch = function() {
    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let points1 = 2 + Rp.bandomInt(6, 2);
    let points2 = 2 + Rp.bandomInt(6, 2);
    let angle = math.random(0, 2 * math.pi);
    let isBordered = math.random(0, 1) < 0.5;

    let lines = [];

    for (let p1 = (isBordered ? 0 : 1); p1 < (isBordered ? points1 + 1 : points1); p1++) {
      let x1 = radius * (-1 + 2 / points1 * p1);
      let y1 = -radius;

      let x2 = x1;
      let y2 = radius;

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));
    }

    for (let p2 = (isBordered ? 0 : 1); p2 < (isBordered ? points2 + 1 : points2); p2++) {
      let x1 = -radius;
      let y1 = radius * (-1 + 2 / points2 * p2);

      let x2 = radius;
      let y2 = y1;

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));
    }

    shape.lines = lines;

    shape.params = {
      points1: points1,
      points2: points2,
      isBordered: isBordered,
      angle: angle
    };

    shape.name = "crosshatch";

    return shape;
  };

  var makeZigzag = function() {
    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let points = 3 + Rp.bandomInt(8, 2);
    let angle = math.random(0, 2 * math.pi);

    let lines = [];

    for (let p = 0; p < points; p++) {

      let x1 = radius * (p % 2 ? 1 : -1);
      let x2 = -x1;

      let y1 = radius - (2 * radius) / points * p;
      let y2 = radius - (2 * radius) / points * (p + 1);

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));
    }

    shape.lines = lines;

    shape.params = {
      points: points,
      angle: angle
    };

    shape.name = "zigzag";

    return shape;
  };

  var makeWavy = function() {
    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let points = 50; // segments
    let angle = math.random(0, 2 * math.pi);

    let freq = math.randomInt(1, 9);
    let ampl = radius;
    let phase = math.random(0, 2 * math.pi);

    // reverse image
    let isHelix = (math.random() < 0.1);

    let lines = [];

    var pif = 2 * math.pi * freq / radius / 2;

    for (let p = 0; p < points; p++) {

      let x1 = -radius + (2 * radius) / points * p;
      let x2 = -radius + (2 * radius) / points * (p + 1);

      let y1 = ampl * math.sin(pif * x1 + phase);
      let y2 = ampl * math.sin(pif * x2 + phase);

      lines.push(makeLine(x1, y1, x2, y2, centre, angle));

      if (isHelix) {
        lines.push(makeLine(x1, -y1, x2, -y2, centre, angle));
      }
    }

    shape.lines = lines;

    shape.params = {
      freq: freq,
      ampl: ampl,
      phase: phase,
      radius: radius,
      helix: isHelix,
      angle: angle
    };

    shape.name = "wavy";

    return shape;
  };

  var makeAsterisk = function() {

    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let linesCount = 2 + Rp.bandomInt(7, 1);
    let angle = math.random(0, 2 * math.pi);

    let lines = [];

    for (let p1 = 0; p1 < linesCount; p1++) {
      lines.push(makeLine(-radius, 0, radius, 0, centre, math.pi * 2 / linesCount * p1 + angle));
    }

    shape.lines = lines;

    shape.params = {
      angle: angle
    };

    shape.name = "asterisk";

    return shape;
  };

  var makeArrow = function() {

    let shape = {};

    var centre = {
      x: 0,
      y: 0
    };

    let radius = math.random(0, 1);
    let angle = math.random(0, 2 * math.pi);

    let lines = [];

    // stem
    lines.push(makeLine(-radius, 0, radius, 0, centre, angle));

    // arrow
    lines.push(makeLine(radius / 2, radius / 2, radius, 0, centre, angle));
    lines.push(makeLine(radius / 2, -radius / 2, radius, 0, centre, angle));

    shape.lines = lines;

    shape.params = {
      angle: angle
    };

    shape.name = "arrow";

    return shape;
  };

  //---

  exports.shapers = [
    {
      fn: function() {

        return {
          name: "polygon",
          make: () => {

            return makePolygon();
          }
        };
      },
      weight: 4,
    },
    {
      fn: function() {

        return {
          name: "crosshatch",
          make: () => {

            return makeCrosshatch();
          }
        };
      },
      weight: 1,
    },
    {
      fn: function() {

        return {
          name: "asterisk",
          make: () => {

            return makeAsterisk();
          }
        };
      },
      weight: 1,
    },
    {
      fn: function() {

        return {
          name: "arrow",
          make: () => {

            return makeArrow();
          }
        };
      },
      weight: 1,
    },
    {
      fn: function() {

        return {
          name: "zigzag",
          make: () => {

            return makeZigzag();
          }
        };
      },
      weight: 1
    },
    {
      fn: function() {

        return {
          name: "wavy",
          make: () => {

            return makeWavy();
          }
        };
      },
      weight: 4
    }
  ];
}());
<|endoftext|>