diff --git a/app/common/lib/siteSuggestions.js b/app/common/lib/siteSuggestions.js
index 9cfc0def82c..567ce4a70c0 100644
--- a/app/common/lib/siteSuggestions.js
+++ b/app/common/lib/siteSuggestions.js
@@ -102,8 +102,9 @@ const tokenizeInput = (data) => {
}
}
- if (url && isUrl(url)) {
- const parsedUrl = urlParse(url.toLowerCase())
+ const parsedUrl = typeof url === 'string' && isUrl(url) && urlParse(url.toLowerCase())
+
+ if (parsedUrl && (parsedUrl.hash || parsedUrl.host || parsedUrl.pathname || parsedUrl.query || parsedUrl.protocol)) {
if (parsedUrl.hash) {
parts.push(parsedUrl.hash.slice(1))
}
diff --git a/js/muonTest.entry.js b/js/muonTest.entry.js
index d449a61a192..1245f5b7f39 100644
--- a/js/muonTest.entry.js
+++ b/js/muonTest.entry.js
@@ -1,81 +1,49 @@
-const urlParse = require('../app/common/urlParse')
-const urlUtil = require('./lib/urlutil')
-const suggestion = require('../app/common/lib/suggestion')
const assert = require('assert')
-
-const assertEqual = (actual, expected, name) => {
- const elem = document.createElement('div')
- elem.id = name
- elem.innerText = 'fail'
-
- try {
- assert.deepEqual(actual, expected)
- elem.innerText = 'success'
- } catch (e) {
- elem.innerText = JSON.stringify(actual)
+const tests = require('../test/muon-native/').run
+
+const testRunner = (successKey) => {
+ const failures = []
+ let assertCount = 0
+
+ const assertWrap = (assertType, assertion, failText) => {
+ assertCount++
+ try {
+ assertion()
+ } catch (e) {
+ failures.push({ assertCount, assertType, failText })
+ }
}
- document.body.appendChild(elem)
-}
-
-const defaultParsedUrl = {
- hash: '',
- host: '',
- hostname: '',
- href: '',
- origin: '',
- path: '/',
- pathname: '/',
- port: '',
- protocol: 'http:',
- query: '',
- search: ''
-}
-
-const runTests = () => {
- assertEqual(urlParse('http://bing.com'),
- Object.assign(defaultParsedUrl, {
- host: 'bing.com',
- hostname: 'bing.com',
- origin: 'http://bing.com/',
- href: 'http://bing.com/'
- }), 'urlParseSimple')
- assertEqual(urlParse('https://brave.com:333/test?abc=123&def#fff'),
- {
- host: 'brave.com:333',
- hostname: 'brave.com',
- origin: 'https://brave.com:333/',
- protocol: 'https:',
- port: '333',
- hash: '#fff',
- pathname: '/test',
- path: '/test?abc=123&def',
- search: '?abc=123&def',
- query: 'abc=123&def',
- href: 'https://brave.com:333/test?abc=123&def#fff'
- }, 'urlParseComplex')
-
- assertEqual(urlParse('http://brave.com%60x.code-fu.org/'),
- Object.assign(defaultParsedUrl, {
- host: 'brave.com%60x.code-fu.org',
- hostname: 'brave.com%60x.code-fu.org',
- href: 'http://brave.com%60x.code-fu.org/',
- origin: 'http://brave.com%60x.code-fu.org/'
- }), 'urlParseIssue10270')
+ const done = () => {
+ const elem = document.createElement('div')
+ // selector must match the pattern used in muonTest.js
+ elem.id = successKey.replace(/[^a-zA-Z0-9_-]/g, '_')
+ // passFail is the critical element, it has to contain either 'PASS' or 'FAIL'
+ const passFail = document.createElement('span')
+ passFail.className = 'passFail'
+ passFail.innerText = 'PASS'
+ const desc = document.createElement('span')
+ desc.innerText = ` ${successKey}`
+ const failure = document.createElement('div')
+ failure.className = 'failure'
+ if (failures.length) {
+ passFail.innerText = 'FAIL'
+ for (let fail of failures) {
+ failure.innerText += `#${fail.assertCount} ${fail.assertType}: ${fail.failText}\n`
+ }
+ }
+ elem.appendChild(passFail)
+ elem.appendChild(desc)
+ elem.appendChild(failure)
+ document.body.appendChild(elem)
+ }
- assertEqual(urlUtil.getOrigin('http://www.brave.com/foo'), 'http://www.brave.com', 'getOriginSimple')
- assertEqual(urlUtil.getOrigin('file:///aaa'), 'file:///', 'getOriginFile')
- assertEqual(urlUtil.getOrigin('http://brave.com:333/foo'), 'http://brave.com:333', 'getOriginWithPort')
- assertEqual(urlUtil.getOrigin('http://127.0.0.1:443/?test=1#abc'), 'http://127.0.0.1:443', 'getOriginIP')
- assertEqual(urlUtil.getOrigin('about:preferences#abc'), 'about:preferences', 'getOriginAbout')
- assertEqual(urlUtil.getOrigin('http://http/test'), 'http://http', 'getOriginSchemeHost')
- assertEqual(urlUtil.getOrigin(''), null, 'getOriginNull')
- assertEqual(urlUtil.getOrigin('abc'), null, 'getOriginInvalid')
- console.log(suggestion.isSimpleDomainNameValue('http://http/test'))
- assertEqual(suggestion.isSimpleDomainNameValue('http://test.com') &&
- suggestion.isSimpleDomainNameValue('http://test.com/') &&
- suggestion.isSimpleDomainNameValue('http://test.com#') &&
- !suggestion.isSimpleDomainNameValue('http://test.com/test'), true, 'suggestionSimpleCheck')
+ return [ 'ok', 'equal', 'deepEqual', 'strictEqual' ].reduce((wrappedAssert, fn) => {
+ wrappedAssert[fn] = (actual, expected) => {
+ assertWrap(fn, () => { assert[fn](actual, expected) }, actual)
+ }
+ return wrappedAssert
+ }, { done })
}
-runTests()
+tests(testRunner)
diff --git a/package.json b/package.json
index 714122026bd..3dca37e820e 100644
--- a/package.json
+++ b/package.json
@@ -177,6 +177,7 @@
"less": "^2.5.3",
"less-loader": "^2.2.1",
"level": "^2.1.1",
+ "lolex": "~1.3.2",
"mkdirp": "^0.5.1",
"mocha": "^2.3.4",
"mockery": "^2.1.0",
diff --git a/test/lib/brave.js b/test/lib/brave.js
index 23339e62d88..3600d2e6260 100644
--- a/test/lib/brave.js
+++ b/test/lib/brave.js
@@ -365,7 +365,7 @@ var exports = {
})
this.app.client.addCommand('waitForTextValue', function (selector, text) {
- logVerbose('waitForSelectedText("' + selector + '", "' + text + '")')
+ logVerbose('waitForTextValue("' + selector + '", "' + text + '")')
return this
.waitForVisible(selector)
.waitUntil(function () {
diff --git a/test/misc-components/muonTest.js b/test/misc-components/muonTest.js
index 3b212cacf86..16a8bad0ac4 100644
--- a/test/misc-components/muonTest.js
+++ b/test/misc-components/muonTest.js
@@ -3,6 +3,7 @@
const Brave = require('../lib/brave')
const testUrl = 'chrome-extension://mnojpmjdmbbfmejpflffifhffcmidifd/muon-tests.html'
+const testCases = require('../muon-native').collect()
function * setup (client) {
yield client.waitForUrl(Brave.newTabUrl).waitForBrowserWindow()
@@ -16,24 +17,46 @@ describe('muon tests', function () {
.tabByIndex(0)
.url(testUrl)
})
- it('muon.url.parse', function * () {
- yield this.app.client
- .waitForTextValue('#urlParseSimple', 'success')
- .waitForTextValue('#urlParseComplex', 'success')
- .waitForTextValue('#urlParseIssue10270', 'success')
- })
- it('urlUtil.getOrigin', function * () {
- yield this.app.client
- .waitForTextValue('#getOriginSimple', 'success')
- .waitForTextValue('#getOriginFile', 'success')
- .waitForTextValue('#getOriginWithPort', 'success')
- .waitForTextValue('#getOriginIP', 'success')
- .waitForTextValue('#getOriginAbout', 'success')
- .waitForTextValue('#getOriginNull', 'success')
- .waitForTextValue('#getOriginInvalid', 'success')
- })
- it('suggestion', function * () {
- yield this.app.client
- .waitForTextValue('#suggestionSimpleCheck', 'success')
- })
+
+ const makeKey = (key, ext) => `${key} → ${ext}`
+ // selector must match the pattern used in muonTest.entry.js
+ const makeSelector = (key) => `#${key.replace(/[^a-zA-Z0-9_-]/g, '_')}`
+
+ const executeTests = (name, successKey, tests) => {
+ const runnableTests = Object.keys(tests).filter((k) => typeof tests[k] === 'function')
+ if (runnableTests.length) {
+ // actual tests, with a `function`
+ for (let testName of runnableTests) {
+ it(testName, function * () {
+ const selector = makeSelector(makeKey(successKey, testName))
+ yield this.app.client.waitForVisible(selector).then(() => {
+ // got the element for this particular test, check the pass/fail and report from there
+ return this.app.client.getText(`${selector}>.passFail`).then((value) => {
+ if (Array.isArray(value)) {
+ // it's possible to have multiple tests with the same key, mainly due to the
+ // selector character clobbering, these show up as an array of elements
+ throw new Error(`Multiple tests with same selector "${selector}", please modify their names`)
+ }
+ if (value !== 'PASS') {
+ return this.app.client.getText(`${selector}>.failure`).then((value) => {
+ // attempt to get at least some of the error message from the .failure element to report back
+ throw new Error(value)
+ })
+ }
+ })
+ })
+ })
+ }
+ }
+
+ const testGroups = Object.keys(tests).filter((k) => typeof tests[k] === 'object')
+ for (let groupKey of testGroups) {
+ // groups of tests, nested objects presumably containing functions
+ describe(groupKey, () => {
+ executeTests(groupKey, makeKey(successKey, groupKey), tests[groupKey])
+ })
+ }
+ }
+
+ executeTests('muon', 'muon', testCases)
})
diff --git a/test/muon-native/index.js b/test/muon-native/index.js
new file mode 100644
index 00000000000..1b6b438269b
--- /dev/null
+++ b/test/muon-native/index.js
@@ -0,0 +1,89 @@
+// test cases in this directory, should be resolvable with `require('./X')`
+const testCases = [
+ 'urlParseTest',
+ 'urlutilTest',
+ 'suggestionTest',
+ 'siteSuggestionTest'
+]
+
+// load all of the tests into a single large nested object
+const collect = () => {
+ const testList = {}
+ for (let testCase of testCases) {
+ testList[testCase] = require(`./${testCase}`)
+ }
+ return testList
+}
+
+const run = (testRunner) => {
+ // first collect an array of ordered runnable tests, each is async-ish in that it has a
+ // callback but the underlying test may not, we just treat them all that way
+ const runQueue = []
+
+ const buildRunQueue = (successKey, tests) => {
+ const ctx = {} // for `this` within a current nested block
+
+ const makeTestExecutor = (testName, key) => {
+ const beforeAfter = testName === 'before' || testName === 'after'
+ const testFn = tests[testName]
+
+ runQueue.push((cb) => {
+ const thisRunner = testRunner(key)
+ // before and after are async if they have 1 arg, a callback
+ // all other tests take a `test` argument first and if they have a second argument it's a callback
+ if (testFn.length > (beforeAfter ? 0 : 1)) {
+ const _cb = (err) => {
+ thisRunner.ok(!err)
+ thisRunner.done()
+ cb()
+ }
+ testFn.call(ctx, beforeAfter ? _cb : thisRunner, beforeAfter ? undefined : _cb)
+ } else {
+ testFn.call(ctx, beforeAfter ? undefined : thisRunner)
+ thisRunner.done()
+ cb()
+ }
+ })
+ }
+
+ // before() always goes first within a block
+ if (typeof tests.before === 'function') {
+ makeTestExecutor('before', `${successKey} → before`)
+ }
+
+ for (let name of Object.keys(tests)) {
+ if (name === 'before' || name === 'after') {
+ continue
+ }
+ let key = `${successKey} → ${name}`
+ if (typeof tests[name] === 'object') {
+ buildRunQueue(key, tests[name]) // recursive for a nested block of tests
+ } else {
+ makeTestExecutor(name, key)
+ }
+ }
+
+ // after() always goes last within a block
+ if (typeof tests.after === 'function') {
+ makeTestExecutor('after', `${successKey} → after`)
+ }
+ }
+
+ buildRunQueue('muon', collect())
+
+ // now we have a queue of ordered tests, execute in order, async-ish
+ const executeQueue = () => {
+ if (!runQueue.length) {
+ return
+ }
+ const fn = runQueue.shift()
+ fn(() => {
+ setImmediate(executeQueue)
+ })
+ }
+
+ executeQueue()
+}
+
+module.exports.collect = collect
+module.exports.run = run
diff --git a/test/muon-native/siteSuggestionTest.js b/test/muon-native/siteSuggestionTest.js
new file mode 100644
index 00000000000..ed8078deacb
--- /dev/null
+++ b/test/muon-native/siteSuggestionTest.js
@@ -0,0 +1 @@
+module.exports = require('../unit/app/common/lib/siteSuggestionsTestComponents')
diff --git a/test/muon-native/suggestionTest.js b/test/muon-native/suggestionTest.js
new file mode 100644
index 00000000000..c0640fa2dcb
--- /dev/null
+++ b/test/muon-native/suggestionTest.js
@@ -0,0 +1,12 @@
+// lazy load requires for dual use in and outside muon
+const suggestion = () => require('../../app/common/lib/suggestion')
+
+module.exports = {
+ suggestionSimpleCheck: (test) => {
+ test.deepEqual(suggestion().isSimpleDomainNameValue('http://test.com') &&
+ suggestion().isSimpleDomainNameValue('http://test.com/') &&
+ suggestion().isSimpleDomainNameValue('http://test.com#') &&
+ !suggestion().isSimpleDomainNameValue('http://test.com/test'),
+ true)
+ }
+}
diff --git a/test/muon-native/urlParseTest.js b/test/muon-native/urlParseTest.js
new file mode 100644
index 00000000000..086c9528ad9
--- /dev/null
+++ b/test/muon-native/urlParseTest.js
@@ -0,0 +1,50 @@
+// lazy load requires for dual use in and outside muon
+const urlParse = () => require('../../app/common/urlParse')
+
+const defaultParsedUrl = {
+ hash: '',
+ host: '',
+ hostname: '',
+ href: '',
+ origin: '',
+ path: '/',
+ pathname: '/',
+ port: '',
+ protocol: 'http:',
+ query: '',
+ search: ''
+}
+
+module.exports = {
+ urlParseSimple: (test) => {
+ test.deepEqual(urlParse()('http://bing.com'), Object.assign(defaultParsedUrl, {
+ host: 'bing.com',
+ hostname: 'bing.com',
+ origin: 'http://bing.com/',
+ href: 'http://bing.com/'
+ }))
+ },
+ urlParseComplex: (test) => {
+ test.deepEqual(urlParse()('https://brave.com:333/test?abc=123&def#fff'), {
+ host: 'brave.com:333',
+ hostname: 'brave.com',
+ origin: 'https://brave.com:333/',
+ protocol: 'https:',
+ port: '333',
+ hash: '#fff',
+ pathname: '/test',
+ path: '/test?abc=123&def',
+ search: '?abc=123&def',
+ query: 'abc=123&def',
+ href: 'https://brave.com:333/test?abc=123&def#fff'
+ })
+ },
+ urlParseIssue10270: (test) => {
+ test.deepEqual(urlParse()('http://brave.com%60x.code-fu.org/'), Object.assign(defaultParsedUrl, {
+ host: 'brave.com%60x.code-fu.org',
+ hostname: 'brave.com%60x.code-fu.org',
+ href: 'http://brave.com%60x.code-fu.org/',
+ origin: 'http://brave.com%60x.code-fu.org/'
+ }))
+ }
+}
diff --git a/test/muon-native/urlutilTest.js b/test/muon-native/urlutilTest.js
new file mode 100644
index 00000000000..8789d34d752
--- /dev/null
+++ b/test/muon-native/urlutilTest.js
@@ -0,0 +1 @@
+module.exports = require('../unit/lib/urlutilTestComponents')
diff --git a/test/unit/app/common/lib/siteSuggestionsTest.js b/test/unit/app/common/lib/siteSuggestionsTest.js
index 39593a8db00..8846be53ef1 100644
--- a/test/unit/app/common/lib/siteSuggestionsTest.js
+++ b/test/unit/app/common/lib/siteSuggestionsTest.js
@@ -1,55 +1,10 @@
-/* global describe, before, after, it */
-const {tokenizeInput, init, query, add} = require('../../../../../app/common/lib/siteSuggestions')
-const assert = require('assert')
-const Immutable = require('immutable')
-const sinon = require('sinon')
+/* global describe, before, after */
+
+const runMuonCompatibleTests = require('../../../runMuonCompatibleTests')
+const components = require('./siteSuggestionsTestComponents')
const fakeElectron = require('../../../lib/fakeElectron')
const mockery = require('mockery')
-const site1 = {
- location: 'https://www.bradrichter.co/bad_numbers/3',
- title: 'Do not use 3 for items because it is prime'
-}
-const site2 = {
- location: 'https://www.brave.com',
- title: 'No really, take back the web'
-}
-const site3 = {
- location: 'https://www.bradrichter.co/bad_numbers/5',
- title: 'Do not use 5 it is so bad, try 6 instead. Much better.'
-}
-
-const site4 = {
- location: 'https://www.designers.com/brad',
- title: 'Brad Saves The World!',
- count: 50
-}
-
-// Same as site4 but added after in init, should be ignored.
-const site5 = {
- location: 'https://www.designers.com/brad',
- title: 'Brad Saves The World!'
-}
-
-// Compares 2 sites via deepEqual while first clearing out cached data
-const siteEqual = (actual, expected) => {
- assert.equal(actual.constructor, expected.constructor)
- if (expected.constructor === Array) {
- assert.equal(actual.length, expected.length)
- for (let i = 0; i < actual.length; i++) {
- assert.deepEqual(actual[i].delete('parsedUrl').toJS(), expected[i].delete('parsedUrl').toJS())
- }
- } else {
- const a = Object.assign({}, actual)
- delete a.parsedUrl
- const e = Object.assign({}, expected)
- delete e.parsedUrl
- assert.deepEqual(a, e)
- }
-}
-
-require('../../../braveUnit')
-
describe('siteSuggestions lib', function () {
before(function () {
mockery.enable({
@@ -62,304 +17,6 @@ describe('siteSuggestions lib', function () {
after(function () {
mockery.disable()
})
- describe('tokenizeInput', function () {
- it('empty string has no tokens', function () {
- assert.deepEqual(tokenizeInput(''), [])
- })
- it('undefined has no tokens', function () {
- assert.deepEqual(tokenizeInput(null), [])
- })
- it('null has no tokens', function () {
- assert.deepEqual(tokenizeInput(undefined), [])
- })
- it('lowercases tokens', function () {
- assert.deepEqual(tokenizeInput('BRaD HaTES PRIMES'), ['brad', 'hates', 'primes'])
- })
- it('includes protocol', function () {
- assert.deepEqual(tokenizeInput('https://bradrichter.co/I/hate/primes.html'), ['bradrichter', 'co', 'i', 'hate', 'primes', 'html', 'https:'])
- })
- it('includes query', function () {
- assert.deepEqual(tokenizeInput('https://bradrichter.co/I/hate/primes.html?test=abc&test2=abcd'), ['bradrichter', 'co', 'i', 'hate', 'primes', 'html', 'test', 'abc', 'test2', 'abcd', 'https:'])
- })
- it('does not include hash', function () {
- assert.deepEqual(tokenizeInput('https://bradrichter.co/I/hate/primes.html?test=abc#testing'), ['testing', 'bradrichter', 'co', 'i', 'hate', 'primes', 'html', 'test', 'abc', 'https:'])
- })
- it('spaces get tokenized', function () {
- assert.deepEqual(tokenizeInput('brad\thates primes'), ['brad', 'hates', 'primes'])
- })
- it('periods get tokenized', function () {
- assert.deepEqual(tokenizeInput('brad.hates.primes'), ['brad', 'hates', 'primes'])
- })
- it('/ gets tokenized', function () {
- assert.deepEqual(tokenizeInput('brad/hates/primes'), ['brad', 'hates', 'primes'])
- })
- it('\\ gets tokenized', function () {
- assert.deepEqual(tokenizeInput('brad\\hates\\primes'), ['brad', 'hates', 'primes'])
- })
- it('can tokenize site objects', function () {
- assert.deepEqual(tokenizeInput(Immutable.fromJS(site1)), ['do', 'not', 'use', '3', 'for', 'items', 'because', 'it', 'is', 'prime', 'www', 'bradrichter', 'co', 'bad_numbers', '3', 'https:'])
- })
- it('non URLs get tokenized', function () {
- assert.deepEqual(tokenizeInput('hello world Greatest...Boss...Ever'), ['hello', 'world', 'greatest', 'boss', 'ever'])
- })
- })
-
- const checkResult = (inputQuery, expectedResults, cb) => {
- query(inputQuery).then((results) => {
- siteEqual(results, expectedResults)
- cb()
- })
- }
-
- describe('not initialized query', function () {
- it('returns no results if not initialized', function (cb) {
- checkResult('hello', [], cb)
- })
- })
-
- describe('query', function () {
- let sites
- before(function (cb) {
- sites = Immutable.fromJS([site1, site2, site3, site4, site5])
- this.clock = sinon.useFakeTimers()
- init(sites).then(cb.bind(null, null))
- this.clock.tick(1510)
- })
- after(function () {
- this.clock.restore()
- })
- it('can query with empty string', function (cb) {
- checkResult('', [], cb)
- })
- it('can query with null', function (cb) {
- checkResult(null, [], cb)
- })
- it('can query with undefined', function (cb) {
- checkResult(undefined, [], cb)
- })
- it('returns an empty array when there are no matches', function (cb) {
- checkResult('hello', [], cb)
- })
- it('returns matched result on an exact token', function (cb) {
- checkResult('bradrichter', [sites.get(0), sites.get(2)], cb)
- })
- it('returns matched result on a token prefix', function (cb) {
- checkResult('brad', [sites.get(0), sites.get(2), sites.get(3).delete('count')], cb)
- })
- it('returns no results on input that has a token as a prefix', function (cb) {
- checkResult('bradrichterhatesprimes.com', [], cb)
- })
- it('can query on title', function (cb) {
- checkResult('back', [sites.get(1)], cb)
- })
- it('can query on multiple tokens in different order', function (cb) {
- checkResult('back really', [sites.get(1)], cb)
- })
- it('all tokens must match, not just some', function (cb) {
- checkResult('brave brad', [], cb)
- })
- })
-
- describe('query', function () {
- describe('sorts results by location', function () {
- before(function (cb) {
- const sites = Immutable.fromJS([{
- location: 'https://brave.com/twi'
- }, {
- location: 'https://twitter.com/brave'
- }, {
- location: 'https://twitter.com/brianbondy'
- }, {
- location: 'https://twitter.com/_brianclif'
- }, {
- location: 'https://twitter.com/cezaraugusto'
- }, {
- location: 'https://bbondy.com/twitter'
- }, {
- location: 'https://twitter.com'
- }, {
- location: 'https://twitter.com/i/moments'
- }])
- init(sites).then(cb.bind(null, null))
- })
- it('orders shortest match first', function (cb) {
- query('twitter.com').then((results) => {
- siteEqual(results[0], Immutable.fromJS({ location: 'https://twitter.com' }))
- cb()
- })
- })
- it('matches prefixes first', function (cb) {
- query('twi').then((results) => {
- siteEqual(results[0], Immutable.fromJS({ location: 'https://twitter.com' }))
- cb()
- })
- })
- it('closest to the left match wins', function (cb) {
- query('twitter.com brian').then((results) => {
- siteEqual(results[0], Immutable.fromJS({ location: 'https://twitter.com/brianbondy' }))
- cb()
- })
- })
- it('matches based on tokens and not exactly', function (cb) {
- query('twitter.com/moments').then((results) => {
- siteEqual(results[0], Immutable.fromJS({ location: 'https://twitter.com/i/moments' }))
- cb()
- })
- })
- })
- describe('sorts results by count', function () {
- describe('with lastAccessedTime', function () {
- before(function (cb) {
- const lastAccessedTime = 1494958046427
- this.page2 = {
- location: 'https://brave.com/page2',
- lastAccessedTime,
- count: 20
- }
- const sites = Immutable.fromJS([{
- location: 'https://brave.com/page1',
- lastAccessedTime,
- count: 5
- }, this.page2, {
- location: 'https://brave.com/page3',
- lastAccessedTime,
- count: 2
- }])
- init(sites).then(cb.bind(null, null))
- })
- it('highest count first', function (cb) {
- query('https://brave.com/page').then((results) => {
- siteEqual(results[0], Immutable.fromJS(this.page2))
- cb()
- })
- })
- })
- describe('without last access time', function () {
- before(function (cb) {
- this.page2 = {
- location: 'https://brave.com/page2',
- count: 20
- }
- const sites = Immutable.fromJS([{
- location: 'https://brave.com/page1',
- count: 5
- }, this.page2, {
- location: 'https://brave.com/page3',
- count: 2
- }])
- init(sites).then(cb.bind(null, null))
- })
- it('highest count first', function (cb) {
- query('https://brave.com/page').then((results) => {
- siteEqual(results[0], Immutable.fromJS(this.page2))
- cb()
- })
- })
- })
- })
- describe('sorts results by lastAccessTime', function () {
- describe('with counts', function () {
- before(function (cb) {
- this.site = {
- location: 'https://bravebrowser.com/page2',
- lastAccessedTime: 1494958046427, // most recent
- count: 1
- }
- const sites = Immutable.fromJS([{
- location: 'https://bravez.com/page1',
- lastAccessedTime: 1,
- count: 1
- }, {
- location: 'https://bravebrowser.com/page1',
- lastAccessedTime: 1494957046426,
- count: 1
- }, this.site, {
- location: 'https://bravebrowser.com/page3',
- lastAccessedTime: 1494957046437,
- count: 1
- }])
- init(sites).then(cb.bind(null, null))
- })
- it('items with lastAccessTime of 1 get ignored (signifies preloaded default)', function (cb) {
- query('https://bravez.com/page').then((results) => {
- assert.equal(results.length, 0)
- cb()
- })
- })
- it('most recently accessed get sorted first', function (cb) {
- query('bravebrowser').then((results) => {
- siteEqual(results[0], Immutable.fromJS(this.site))
- cb()
- })
- })
- })
- describe('without counts', function () {
- before(function (cb) {
- this.site = {
- location: 'https://bravebrowser.com/page2',
- lastAccessedTime: 1494958046427 // most recent
- }
- const sites = Immutable.fromJS([{
- location: 'https://bravez.com/page1',
- lastAccessedTime: 1
- }, {
- location: 'https://bravebrowser.com/page1',
- lastAccessedTime: 1494957046426
- }, this.site, {
- location: 'https://bravebrowser.com/page3',
- lastAccessedTime: 1494957046437
- }])
- init(sites).then(cb.bind(null, null))
- })
- it('items with lastAccessTime of 1 get ignored (signifies preloaded default)', function (cb) {
- query('https://bravez.com/page').then((results) => {
- assert.equal(results.length, 0)
- cb()
- })
- })
- it('most recently accessed get sorted first', function (cb) {
- query('bravebrowser').then((results) => {
- siteEqual(results[0], Immutable.fromJS(this.site))
- cb()
- })
- })
- })
- })
- })
-
- describe('add sites after init', function () {
- before(function (cb) {
- const sites = [site1, site2, site3, site4]
- init(sites).then(() => {
- add({
- location: 'https://slack.com'
- })
- }).then(cb.bind(null, null))
- })
- it('can be found', function (cb) {
- checkResult('slack', [Immutable.fromJS({ location: 'https://slack.com' })], cb)
- })
- it('adding twice results in 1 result only with latest results', function (cb) {
- const newSite = {
- location: 'https://slack.com',
- count: 30,
- title: 'SlickSlack'
- }
- add(newSite)
- checkResult('slack', [Immutable.fromJS(newSite)], cb)
- })
- it('can add simple strings', function (cb) {
- add({
- location: 'https://slashdot.org'
- })
- checkResult('slash', [Immutable.fromJS({ location: 'https://slashdot.org' })], cb)
- })
- it('can add Immutable objects', function (cb) {
- add(Immutable.fromJS({
- location: 'https://microsoft.com'
- }))
- checkResult('micro', [Immutable.fromJS({ location: 'https://microsoft.com' })], cb)
- })
- })
+ runMuonCompatibleTests('urlutil', components)
})
diff --git a/test/unit/app/common/lib/siteSuggestionsTestComponents.js b/test/unit/app/common/lib/siteSuggestionsTestComponents.js
new file mode 100644
index 00000000000..76591ae4df0
--- /dev/null
+++ b/test/unit/app/common/lib/siteSuggestionsTestComponents.js
@@ -0,0 +1,338 @@
+const {tokenizeInput, init, query, add} = require('../../../../../app/common/lib/siteSuggestions')
+const Immutable = require('immutable')
+const lolex = require('lolex')
+
+const site1 = {
+ location: 'https://www.bradrichter.co/bad_numbers/3',
+ title: 'Do not use 3 for items because it is prime'
+}
+const site2 = {
+ location: 'https://www.brave.com',
+ title: 'No really, take back the web'
+}
+const site3 = {
+ location: 'https://www.bradrichter.co/bad_numbers/5',
+ title: 'Do not use 5 it is so bad, try 6 instead. Much better.'
+}
+const site4 = {
+ location: 'https://www.designers.com/brad',
+ title: 'Brad Saves The World!',
+ count: 50
+}
+// Same as site4 but added after in init, should be ignored.
+const site5 = {
+ location: 'https://www.designers.com/brad',
+ title: 'Brad Saves The World!'
+}
+const sites = Immutable.fromJS([site1, site2, site3, site4, site5])
+
+// Compares 2 sites via deepEqual while first clearing out cached data
+const siteEqual = (test, actual, expected) => {
+ test.equal(actual.constructor, expected.constructor)
+ if (expected.constructor === Array) {
+ test.equal(actual.length, expected.length)
+ for (let i = 0; i < actual.length; i++) {
+ test.deepEqual(actual[i].delete('parsedUrl').toJS(), expected[i].delete('parsedUrl').toJS())
+ }
+ } else {
+ const a = Object.assign({}, actual)
+ delete a.parsedUrl
+ const e = Object.assign({}, expected)
+ delete e.parsedUrl
+ test.deepEqual(a, e)
+ }
+}
+
+const checkResult = (test, inputQuery, expectedResults, cb) => {
+ return query(inputQuery).then((results) => {
+ siteEqual(test, results, expectedResults)
+ cb()
+ })
+}
+
+module.exports = {
+ 'tokenizeInput': {
+ 'empty string has no tokens': (test) => {
+ test.deepEqual(tokenizeInput(''), [])
+ },
+ 'undefined has no tokens': (test) => {
+ test.deepEqual(tokenizeInput(null), [])
+ },
+ 'null has no tokens': (test) => {
+ test.deepEqual(tokenizeInput(undefined), [])
+ },
+ 'lowercases tokens': (test) => {
+ test.deepEqual(tokenizeInput('BRaD HaTES PRIMES'), ['brad', 'hates', 'primes'])
+ },
+ 'includes protocol': (test) => {
+ test.deepEqual(tokenizeInput('https://bradrichter.co/I/hate/primes.html'), ['bradrichter', 'co', 'i', 'hate', 'primes', 'html', 'https:'])
+ },
+ 'includes query': (test) => {
+ test.deepEqual(tokenizeInput('https://bradrichter.co/I/hate/primes.html?test=abc&test2=abcd'), ['bradrichter', 'co', 'i', 'hate', 'primes', 'html', 'test', 'abc', 'test2', 'abcd', 'https:'])
+ },
+ 'does not include hash': (test) => {
+ test.deepEqual(tokenizeInput('https://bradrichter.co/I/hate/primes.html?test=abc#testing'), ['testing', 'bradrichter', 'co', 'i', 'hate', 'primes', 'html', 'test', 'abc', 'https:'])
+ },
+ 'spaces get tokenized': (test) => {
+ test.deepEqual(tokenizeInput('brad\thates primes'), ['brad', 'hates', 'primes'])
+ },
+ 'periods get tokenized': (test) => {
+ test.deepEqual(tokenizeInput('brad.hates.primes'), ['brad', 'hates', 'primes'])
+ },
+ 'forward slash gets tokenized': (test) => {
+ test.deepEqual(tokenizeInput('brad/hates/primes'), ['brad', 'hates', 'primes'])
+ },
+ 'backslash gets tokenized': (test) => {
+ test.deepEqual(tokenizeInput('brad\\hates\\primes'), ['brad', 'hates', 'primes'])
+ },
+ 'can tokenize site objects': (test) => {
+ test.deepEqual(tokenizeInput(Immutable.fromJS(site1)), ['do', 'not', 'use', '3', 'for', 'items', 'because', 'it', 'is', 'prime', 'www', 'bradrichter', 'co', 'bad_numbers', '3', 'https:'])
+ },
+ 'non URLs get tokenized': (test) => {
+ test.deepEqual(tokenizeInput('hello world Greatest...Boss...Ever'), ['hello', 'world', 'greatest', 'boss', 'ever'])
+ }
+ },
+
+ 'not initialized query': {
+ 'returns no results if not initialized': (test, cb) => {
+ checkResult(test, 'hello', [], cb).catch(cb)
+ }
+ },
+
+ 'basic query': {
+ before: function (cb) {
+ this.clock = lolex.install()
+ init(sites).then(cb.bind(null, null))
+ this.clock.tick(1510)
+ },
+ after: function () {
+ this.clock.uninstall()
+ },
+ 'can query with empty string': (test, cb) => {
+ checkResult(test, '', [], cb).catch(cb)
+ },
+ 'can query with null': (test, cb) => {
+ checkResult(test, null, [], cb).catch(cb)
+ },
+ 'can query with undefined': (test, cb) => {
+ checkResult(test, undefined, [], cb).catch(cb)
+ },
+ 'returns an empty array when there are no matches': (test, cb) => {
+ checkResult(test, 'hello', [], cb).catch(cb)
+ },
+ 'returns matched result on an exact token': (test, cb) => {
+ checkResult(test, 'bradrichter', [sites.get(0), sites.get(2)], cb).catch(cb)
+ },
+ 'returns matched result on a token prefix': (test, cb) => {
+ checkResult(test, 'brad', [sites.get(0), sites.get(2), sites.get(3).delete('count')], cb).catch(cb)
+ },
+ 'returns no results on input that has a token as a prefix': (test, cb) => {
+ checkResult(test, 'bradrichterhatesprimes.com', [], cb).catch(cb)
+ },
+ 'can query on title': (test, cb) => {
+ checkResult(test, 'back', [sites.get(1)], cb).catch(cb)
+ },
+ 'can query on multiple tokens in different order': (test, cb) => {
+ checkResult(test, 'back really', [sites.get(1)], cb).catch(cb)
+ },
+ 'all tokens must match, not just some': (test, cb) => {
+ checkResult(test, 'brave brad', [], cb).catch(cb)
+ }
+ },
+
+ 'query': {
+ 'sorts results by location': {
+ before: (cb) => {
+ const sites = Immutable.fromJS([{
+ location: 'https://brave.com/twi'
+ }, {
+ location: 'https://twitter.com/brave'
+ }, {
+ location: 'https://twitter.com/brianbondy'
+ }, {
+ location: 'https://twitter.com/_brianclif'
+ }, {
+ location: 'https://twitter.com/cezaraugusto'
+ }, {
+ location: 'https://bbondy.com/twitter'
+ }, {
+ location: 'https://twitter.com'
+ }, {
+ location: 'https://twitter.com/i/moments'
+ }])
+ init(sites).then(cb.bind(null, null))
+ },
+ 'orders shortest match first': (test, cb) => {
+ query('twitter.com').then((results) => {
+ siteEqual(test, results[0], Immutable.fromJS({ location: 'https://twitter.com' }))
+ cb()
+ }).catch(cb)
+ },
+ 'matches prefixes first': (test, cb) => {
+ query('twi').then((results) => {
+ siteEqual(test, results[0], Immutable.fromJS({ location: 'https://twitter.com' }))
+ cb()
+ }).catch(cb)
+ },
+ 'closest to the left match wins': (test, cb) => {
+ query('twitter.com brian').then((results) => {
+ siteEqual(test, results[0], Immutable.fromJS({ location: 'https://twitter.com/brianbondy' }))
+ cb()
+ }).catch(cb)
+ },
+ 'matches based on tokens and not exactly': (test, cb) => {
+ query('twitter.com/moments').then((results) => {
+ siteEqual(test, results[0], Immutable.fromJS({ location: 'https://twitter.com/i/moments' }))
+ cb()
+ }).catch(cb)
+ }
+ },
+ 'sorts results by count': {
+ 'with lastAccessedTime': {
+ before: function (cb) {
+ const lastAccessedTime = 1494958046427
+ this.page2 = {
+ location: 'https://brave.com/page2',
+ lastAccessedTime,
+ count: 20
+ }
+ const sites = Immutable.fromJS([{
+ location: 'https://brave.com/page1',
+ lastAccessedTime,
+ count: 5
+ }, this.page2, {
+ location: 'https://brave.com/page3',
+ lastAccessedTime,
+ count: 2
+ }])
+ init(sites).then(cb.bind(null, null))
+ },
+ 'highest count first': function (test, cb) {
+ query('https://brave.com/page').then((results) => {
+ siteEqual(test, results[0], Immutable.fromJS(this.page2))
+ cb()
+ }).catch(cb)
+ }
+ },
+ 'without last access time': {
+ before: function (cb) {
+ this.page2 = {
+ location: 'https://brave.com/page2',
+ count: 20
+ }
+ const sites = Immutable.fromJS([{
+ location: 'https://brave.com/page1',
+ count: 5
+ }, this.page2, {
+ location: 'https://brave.com/page3',
+ count: 2
+ }])
+ init(sites).then(cb.bind(null, null))
+ },
+ 'highest count first': function (test, cb) {
+ query('https://brave.com/page').then((results) => {
+ siteEqual(test, results[0], Immutable.fromJS(this.page2))
+ cb()
+ }).catch(cb)
+ }
+ }
+ },
+ 'sorts results by lastAccessTime': {
+ 'with counts': {
+ before: function (cb) {
+ this.site = {
+ location: 'https://bravebrowser.com/page2',
+ lastAccessedTime: 1494958046427, // most recent
+ count: 1
+ }
+ const sites = Immutable.fromJS([{
+ location: 'https://bravez.com/page1',
+ lastAccessedTime: 1,
+ count: 1
+ }, {
+ location: 'https://bravebrowser.com/page1',
+ lastAccessedTime: 1494957046426,
+ count: 1
+ }, this.site, {
+ location: 'https://bravebrowser.com/page3',
+ lastAccessedTime: 1494957046437,
+ count: 1
+ }])
+ init(sites).then(cb.bind(null, null))
+ },
+ 'items with lastAccessTime of 1 get ignored (signifies preloaded default)': (test, cb) => {
+ query('https://bravez.com/page').then((results) => {
+ test.equal(results.length, 0)
+ cb()
+ }).catch(cb)
+ },
+ 'most recently accessed get sorted first': function (test, cb) {
+ query('bravebrowser').then((results) => {
+ siteEqual(test, results[0], Immutable.fromJS(this.site))
+ cb()
+ }).catch(cb)
+ }
+ },
+ 'without counts': {
+ before: function (cb) {
+ this.site = {
+ location: 'https://bravebrowser.com/page2',
+ lastAccessedTime: 1494958046427 // most recent
+ }
+ const sites = Immutable.fromJS([{
+ location: 'https://bravez.com/page1',
+ lastAccessedTime: 1
+ }, {
+ location: 'https://bravebrowser.com/page1',
+ lastAccessedTime: 1494957046426
+ }, this.site, {
+ location: 'https://bravebrowser.com/page3',
+ lastAccessedTime: 1494957046437
+ }])
+ init(sites).then(cb.bind(null, null))
+ },
+ 'items with lastAccessTime of 1 get ignored (signifies preloaded default)': (test, cb) => {
+ query('https://bravez.com/page').then((results) => {
+ test.equal(results.length, 0)
+ cb()
+ }).catch(cb)
+ },
+ 'most recently accessed get sorted first': function (test, cb) {
+ query('bravebrowser').then((results) => {
+ siteEqual(test, results[0], Immutable.fromJS(this.site))
+ cb()
+ }).catch(cb)
+ }
+ }
+ }
+ },
+
+ 'add sites after init': {
+ before: (cb) => {
+ const sites = [site1, site2, site3, site4]
+ init(sites).then(() => {
+ add({ location: 'https://slack.com' })
+ }).then(cb.bind(null, null))
+ },
+ 'can be found': (test, cb) => {
+ checkResult(test, 'slack', [Immutable.fromJS({ location: 'https://slack.com' })], cb).catch(cb)
+ },
+ 'adding twice results in 1 result only with latest results': (test, cb) => {
+ const newSite = {
+ location: 'https://slack.com',
+ count: 30,
+ title: 'SlickSlack'
+ }
+ add(newSite)
+ checkResult(test, 'slack', [Immutable.fromJS(newSite)], cb).catch(cb)
+ },
+ 'can add simple strings': (test, cb) => {
+ add({ location: 'https://slashdot.org' })
+ checkResult(test, 'slash', [Immutable.fromJS({ location: 'https://slashdot.org' })], cb).catch(cb)
+ },
+ 'can add Immutable objects': (test, cb) => {
+ add(Immutable.fromJS({ location: 'https://microsoft.com' }))
+ checkResult(test, 'micro', [Immutable.fromJS({ location: 'https://microsoft.com' })], cb).catch(cb)
+ }
+ }
+}
diff --git a/test/unit/lib/urlutilTest.js b/test/unit/lib/urlutilTest.js
index 459094e951d..c103852f11c 100644
--- a/test/unit/lib/urlutilTest.js
+++ b/test/unit/lib/urlutilTest.js
@@ -1,565 +1,4 @@
-/* global describe, it */
-const urlUtil = require('../../../js/lib/urlutil')
-const assert = require('assert')
+const runMuonCompatibleTests = require('../runMuonCompatibleTests')
+const components = require('./urlutilTestComponents')
-require('../braveUnit')
-
-describe('urlutil', function () {
- describe('getScheme', function () {
- it('null for empty', function () {
- assert.equal(urlUtil.getScheme('/file/path/to/file'), null)
- })
- it('localhost: for localhost', function () {
- assert.equal(urlUtil.getScheme('localhost://127.0.0.1'), 'localhost:')
- })
- it('gets scheme with :', function () {
- assert.equal(urlUtil.getScheme('data:datauri'), 'data:')
- })
- it('host:port is not recognized as a scheme', function () {
- assert.equal(urlUtil.getScheme('localhost:8089'), null)
- })
- it('gets scheme with ://', function () {
- assert.equal(urlUtil.getScheme('http://www.brave.com'), 'http://')
- })
- })
-
- describe('prependScheme', function () {
- it('returns null when input is null', function () {
- assert.equal(urlUtil.prependScheme(null), null)
- })
- it('returns undefined when input is undefined', function () {
- assert.equal(urlUtil.prependScheme(), undefined)
- })
- it('prepends file:// to absolute file path', function () {
- assert.equal(urlUtil.prependScheme('/file/path/to/file'), 'file:///file/path/to/file')
- })
- it('defaults to http://', function () {
- assert.equal(urlUtil.prependScheme('www.brave.com'), 'http://www.brave.com')
- })
- it('keeps schema if already exists', function () {
- assert.equal(urlUtil.prependScheme('https://www.brave.com'), 'https://www.brave.com')
- })
- })
-
- describe('isNotURL', function () {
- describe('returns false when input:', function () {
- it('is a valid URL', function () {
- assert.equal(urlUtil.isNotURL('brave.com'), false)
- })
- it('is an absolute file path without scheme', function () {
- assert.equal(urlUtil.isNotURL('/file/path/to/file'), false)
- })
- it('is an absolute file path with scheme', function () {
- assert.equal(urlUtil.isNotURL('file:///file/path/to/file'), false)
- })
- describe('for special pages', function () {
- it('is a data URI', function () {
- assert.equal(urlUtil.isNotURL('data:text/html,hi'), false)
- })
- it('is a view source URL', function () {
- assert.equal(urlUtil.isNotURL('view-source://url-here'), false)
- })
- it('is a mailto link', function () {
- assert.equal(urlUtil.isNotURL('mailto:brian@brave.com'), false)
- })
- it('is an about page', function () {
- assert.equal(urlUtil.isNotURL('about:preferences'), false)
- })
- it('is a chrome-extension page', function () {
- assert.equal(urlUtil.isNotURL('chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/cast_sender.js'), false)
- })
- it('is a magnet URL', function () {
- assert.equal(urlUtil.isNotURL('chrome://gpu'), false)
- })
- it('is a chrome page', function () {
- assert.equal(urlUtil.isNotURL('magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C'), false)
- })
- })
- it('contains a hostname and port number', function () {
- assert.equal(urlUtil.isNotURL('someBraveServer:8089'), false)
- })
- it('starts or ends with whitespace', function () {
- assert.equal(urlUtil.isNotURL(' http://brave.com '), false)
- assert.equal(urlUtil.isNotURL('\n\nhttp://brave.com\n\n'), false)
- assert.equal(urlUtil.isNotURL('\t\thttp://brave.com\t\t'), false)
- })
- it('is a URL which contains basic auth user/pass', function () {
- assert.equal(urlUtil.isNotURL('http://username:password@example.com'), false)
- })
- it('is localhost (case-insensitive)', function () {
- assert.equal(urlUtil.isNotURL('LoCaLhOsT'), false)
- })
- it('is a hostname (not a domain)', function () {
- assert.equal(urlUtil.isNotURL('http://computer001/phpMyAdmin'), false)
- })
- it('ends with period (input contains a forward slash and domain)', function () {
- assert.equal(urlUtil.isNotURL('brave.com/test/cc?_ri_=3vv-8-e.'), false)
- })
- it('is a string with whitespace but has schema', function () {
- assert.equal(urlUtil.isNotURL('https://wwww.brave.com/test space.jpg'), false)
- })
- it('has custom protocol', function () {
- assert.equal(urlUtil.isNotURL('brave://test'), false)
- })
- })
-
- describe('returns true when input:', function () {
- it('is null or undefined', function () {
- assert.equal(urlUtil.isNotURL(), true)
- assert.equal(urlUtil.isNotURL(null), true)
- })
- it('is not a string', function () {
- assert.equal(urlUtil.isNotURL(false), true)
- assert.equal(urlUtil.isNotURL(333.449), true)
- })
- it('is a quoted string', function () {
- assert.equal(urlUtil.isNotURL('"search term here"'), true)
- })
- it('is a pure string (no TLD)', function () {
- assert.equal(urlUtil.isNotURL('brave'), true)
- })
- describe('search query', function () {
- it('starts with ?', function () {
- assert.equal(urlUtil.isNotURL('?brave'), true)
- })
- it('has a question mark followed by a space', function () {
- assert.equal(urlUtil.isNotURL('? brave'), true)
- })
- it('starts with .', function () {
- assert.equal(urlUtil.isNotURL('.brave'), true)
- })
- it('ends with . (input does NOT contain a forward slash)', function () {
- assert.equal(urlUtil.isNotURL('brave.'), true)
- })
- it('ends with period (input contains only a forward slash)', function () {
- assert.equal(urlUtil.isNotURL('brave/com/test/cc?_ri_=3vv-8-e.'), true)
- })
- })
- it('is a string with schema but invalid domain name', function () {
- assert.equal(urlUtil.isNotURL('https://www.bra ve.com/test space.jpg'), true)
- })
- it('contains more than one word', function () {
- assert.equal(urlUtil.isNotURL('brave is cool'), true)
- })
- it('is not an about page / view source / data URI / mailto / etc', function () {
- assert.equal(urlUtil.isNotURL('not-a-chrome-extension:'), true)
- assert.equal(urlUtil.isNotURL('mailtoo:'), true)
- })
- it('is a URL (without protocol) which contains basic auth user/pass', function () {
- assert.equal(urlUtil.isNotURL('username:password@example.com'), true)
- })
- it('has space in schema', function () {
- assert.equal(urlUtil.isNotURL('https ://brave.com'), true)
- })
- })
- })
-
- describe('getUrlFromInput', function () {
- it('returns empty string when input is null', function () {
- assert.equal(urlUtil.getUrlFromInput(null), '')
- })
- it('returns empty string when input is undefined', function () {
- assert.equal(urlUtil.getUrlFromInput(), '')
- })
- it('calls prependScheme', function () {
- assert.equal(urlUtil.getUrlFromInput('/file/path/to/file'), 'file:///file/path/to/file')
- })
- })
-
- describe('isURL', function () {
- it('returns !isNotURL', function () {
- assert.equal(urlUtil.isURL('brave.com'), !urlUtil.isNotURL('brave.com'))
- assert.equal(urlUtil.isURL('brave is cool'), !urlUtil.isNotURL('brave is cool'))
- assert.equal(urlUtil.isURL('mailto:brian@brave.com'), !urlUtil.isNotURL('mailto:brian@brave.com'))
- })
- })
-
- describe('isFileType', function () {
- it('relative file', function () {
- assert.equal(urlUtil.isFileType('/file/abc/test.pdf', 'pdf'), true)
- })
- it('relative path', function () {
- assert.equal(urlUtil.isFileType('/file/abc/test', 'pdf'), false)
- })
- it('JPG URL', function () {
- assert.equal(urlUtil.isFileType('http://example.com/test/ABC.JPG?a=b#test', 'jpg'), true)
- })
- it('non-JPG URL', function () {
- assert.equal(urlUtil.isFileType('http://example.com/test/jpg', 'jpg'), false)
- })
- it('invalid URL', function () {
- assert.equal(urlUtil.isFileType('foo', 'jpg'), false)
- })
- })
-
- describe('toPDFJSLocation', function () {
- const baseUrl = 'chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/'
- it('pdf', function () {
- assert.equal(urlUtil.toPDFJSLocation('http://abc.com/test.pdf'), baseUrl + 'content/web/viewer.html?file=http%3A%2F%2Fabc.com%2Ftest.pdf')
- })
- it('non-pdf', function () {
- assert.equal(urlUtil.toPDFJSLocation('http://abc.com/test.pdf.txt'), 'http://abc.com/test.pdf.txt')
- })
- it('file url', function () {
- assert.equal(urlUtil.toPDFJSLocation('file://abc.com/test.pdf.txt'), 'file://abc.com/test.pdf.txt')
- })
- it('empty', function () {
- assert.equal(urlUtil.toPDFJSLocation(''), '')
- })
- })
-
- describe('getPDFViewerUrl', function () {
- const baseUrl = 'chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/content/web/viewer.html?file='
- it('regular url', function () {
- assert.equal(urlUtil.getPDFViewerUrl('http://example.com'), baseUrl + 'http%3A%2F%2Fexample.com')
- })
- it('file url', function () {
- assert.equal(urlUtil.getPDFViewerUrl('file:///Users/yan/some files/test.pdf'), baseUrl + 'file%3A%2F%2F%2FUsers%2Fyan%2Fsome%20files%2Ftest.pdf')
- })
- })
-
- describe('getHostname', function () {
- it('returns undefined if the URL is invalid', function () {
- assert.equal(urlUtil.getHostname(null), undefined)
- })
- it('returns the host field (including port number)', function () {
- assert.equal(urlUtil.getHostname('https://brave.com:8080/test/'), 'brave.com:8080')
- })
- it('allows you to exclude the port number', function () {
- assert.equal(urlUtil.getHostname('https://brave.com:8080/test/', true), 'brave.com')
- })
- })
-
- describe('getHostnamePatterns', function () {
- it('gets bare domain hostname patterns', function () {
- // XXX: *.com probably should be excluded
- assert.deepEqual(urlUtil.getHostnamePatterns('http://brave.com'),
- ['brave.com', '*.com', 'brave.*'])
- })
- it('gets subdomain hostname patterns', function () {
- assert.deepEqual(urlUtil.getHostnamePatterns('https://bar.brave.com'),
- ['bar.brave.com',
- '*.brave.com',
- 'bar.*.com',
- 'bar.brave.*'])
- assert.deepEqual(urlUtil.getHostnamePatterns('https://foo.bar.brave.com'),
- ['foo.bar.brave.com',
- '*.bar.brave.com',
- 'foo.*.brave.com',
- 'foo.bar.*.com',
- 'foo.bar.brave.*',
- '*.brave.com'])
- })
- })
-
- describe('getLocationIfPDF', function () {
- it('gets location for PDF JS URL', function () {
- assert.equal(urlUtil.getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf'),
- 'https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf')
- })
- it('gets location for PDF JS viewer URL', function () {
- assert.equal(urlUtil.getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/content/web/viewer.html?file=http%3A%2F%2Funec.edu.az%2Fapplication%2Fuploads%2F2014%2F12%2Fpdf-sample.pdf'),
- 'http://unec.edu.az/application/uploads/2014/12/pdf-sample.pdf')
- })
- it('does not remove wayback machine url location for PDF JS URL', function () {
- assert.equal(urlUtil.getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/https://web.archive.org/web/20160106152308/http://stlab.adobe.com/wiki/images/d/d3/Test.pdf'),
- 'https://web.archive.org/web/20160106152308/http://stlab.adobe.com/wiki/images/d/d3/Test.pdf')
- })
- it('does not modify location for non-pdf URL', function () {
- assert.equal(urlUtil.getLocationIfPDF('https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf'),
- 'https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf')
- assert.equal(urlUtil.getLocationIfPDF('chrome-extension://blank'), 'chrome-extension://blank')
- assert.equal(urlUtil.getLocationIfPDF(null), null)
- })
- it('gets location for file: PDF URL', function () {
- let url = 'chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/file:///Users/yan/Downloads/test.pdf'
- assert.equal(urlUtil.getLocationIfPDF(url), 'file:///Users/yan/Downloads/test.pdf')
- })
- })
-
- describe('getDefaultFaviconUrl', function () {
- it('returns empty string if input is not a URL', function () {
- assert.equal(urlUtil.getDefaultFaviconUrl('invalid-url-goes-here'), '')
- })
- it('returns the default favicon URL when given a valid URL', function () {
- assert.equal(urlUtil.getDefaultFaviconUrl('https://brave.com'), 'https://brave.com/favicon.ico')
- })
- it('includes the port in the response when given a valid URL with a port number', function () {
- assert.equal(urlUtil.getDefaultFaviconUrl('https://brave.com:8080'), 'https://brave.com:8080/favicon.ico')
- })
- })
-
- describe('getPunycodeUrl', function () {
- it('returns original string if input is ASCII', function () {
- assert.equal(urlUtil.getPunycodeUrl('invalid-url-goes-here'), 'invalid-url-goes-here')
- })
- it('returns punycode ASCII string if input is non-ASCII', function () {
- assert.equal(urlUtil.getPunycodeUrl('ebаy.com'), 'xn--eby-7cd.com')
- })
- it('returns the punycode URL when given a valid URL', function () {
- assert.equal(urlUtil.getPunycodeUrl('http://brave:brave@ebаy.com:1234/brave#brave'), 'http://brave:brave@xn--eby-7cd.com:1234/brave#brave')
- })
- it('returns the punycode URL when given a URL contains @', function () {
- assert.equal(urlUtil.getPunycodeUrl('ebаy.com/@ebаy.com'), 'xn--eby-7cd.com/@xn--eby-7cd.com')
- })
- })
-
- describe('isPotentialPhishingUrl', function () {
- it('returns false if input is not a URL', function () {
- assert.equal(urlUtil.isPotentialPhishingUrl(null), false)
- })
- it('returns false if input is a regular URL', function () {
- assert.equal(urlUtil.isPotentialPhishingUrl(' https://google.com'), false)
- })
- it('returns true if input is a data URL', function () {
- assert.equal(urlUtil.isPotentialPhishingUrl('data:text/html,'), true)
- })
- it('returns true if input is a blob URL', function () {
- assert.equal(urlUtil.isPotentialPhishingUrl(' BLOB:foo '), true)
- })
- })
-
- describe('isFileScheme', function () {
- describe('returns true when input:', function () {
- it('is an absolute file path with scheme', function () {
- assert.equal(urlUtil.isFileScheme('file:///file/path/to/file'), true)
- })
- })
- describe('returns false when input:', function () {
- it('is an absolute file path without scheme', function () {
- assert.equal(urlUtil.isFileScheme('/file/path/to/file'), false)
- })
- it('is a URL', function () {
- assert.equal(urlUtil.isFileScheme('http://brave.com'), false)
- })
- it('has custom protocol', function () {
- assert.equal(urlUtil.isFileScheme('brave://test'), false)
- })
- })
- })
-
- describe('getDisplayHost', function () {
- it('url is http', function () {
- const result = urlUtil.getDisplayHost('http://brave.com')
- assert.equal(result, 'brave.com')
- })
-
- it('url is https', function () {
- const result = urlUtil.getDisplayHost('https://brave.com')
- assert.equal(result, 'brave.com')
- })
-
- it('url is file', function () {
- const result = urlUtil.getDisplayHost('file://brave.text')
- assert.equal(result, 'file://brave.text')
- })
- })
-
- describe('getOrigin', function () {
- it('returns file:/// for any file url', function () {
- assert.strictEqual(urlUtil.getOrigin('file://'), 'file:///')
- assert.strictEqual(urlUtil.getOrigin('file:///'), 'file:///')
- assert.strictEqual(urlUtil.getOrigin('file:///some'), 'file:///')
- assert.strictEqual(urlUtil.getOrigin('file:///some/'), 'file:///')
- assert.strictEqual(urlUtil.getOrigin('file:///some/path'), 'file:///')
- })
- it('gets URL origin for simple url', function () {
- assert.strictEqual(urlUtil.getOrigin('https://abc.bing.com'), 'https://abc.bing.com')
- })
- it('gets URL origin for url with port', function () {
- assert.strictEqual(urlUtil.getOrigin('https://bing.com:443/?test=1#abc'), 'https://bing.com:443')
- })
- it('gets URL origin for IP host', function () {
- assert.strictEqual(urlUtil.getOrigin('http://127.0.0.1:443/?test=1#abc'), 'http://127.0.0.1:443')
- })
- it('gets URL origin for slashless protocol URL', function () {
- assert.strictEqual(urlUtil.getOrigin('about:test/foo'), 'about:test')
- })
- it('returns null for invalid URL', function () {
- assert.strictEqual(urlUtil.getOrigin('abc'), null)
- })
- it('returns null for empty URL', function () {
- assert.strictEqual(urlUtil.getOrigin(''), null)
- })
- it('returns null for null URL', function () {
- assert.strictEqual(urlUtil.getOrigin(null), null)
- })
- it('returns correct result for URL with hostname that is a scheme', function () {
- assert.strictEqual(urlUtil.getOrigin('http://http/test'), 'http://http')
- })
- })
-
- describe('stripLocation', function () {
- it('null scenario', function () {
- const result = urlUtil.stripLocation(null)
- assert.equal(result, '')
- })
-
- it('empty string', function () {
- const result = urlUtil.stripLocation('')
- assert.equal(result, '')
- })
-
- it('normal url without # or /', function () {
- const result = urlUtil.stripLocation('https://brave.com')
- assert.equal(result, 'https://brave.com')
- })
-
- it('normal url with # but not at the end', function () {
- const result = urlUtil.stripLocation('https://brave.com#title')
- assert.equal(result, 'https://brave.com#title')
- })
-
- it('normal url with # at the end', function () {
- const result = urlUtil.stripLocation('https://brave.com#')
- assert.equal(result, 'https://brave.com')
- })
-
- it('normal url with / at the end', function () {
- const result = urlUtil.stripLocation('https://brave.com/')
- assert.equal(result, 'https://brave.com')
- })
-
- it('normal url with /# at the end', function () {
- const result = urlUtil.stripLocation('https://brave.com/#')
- assert.equal(result, 'https://brave.com')
- })
-
- it('normal url with white space at the end', function () {
- const result = urlUtil.stripLocation('https://brave.com ')
- assert.equal(result, 'https://brave.com')
- })
- })
-
- describe('parseFaviconDataUrl', function () {
- it('null scenario', function () {
- const result = urlUtil.parseFaviconDataUrl(null)
- assert.equal(result, null)
- })
- it('empty string', function () {
- const result = urlUtil.parseFaviconDataUrl('')
- assert.equal(result, null)
- })
- it('regular URL', function () {
- const result = urlUtil.parseFaviconDataUrl('http://example.com')
- assert.equal(result, null)
- })
- it('non-image data URL', function () {
- const result = urlUtil.parseFaviconDataUrl('data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678')
- assert.equal(result, null)
- })
- it('non-base64 data URL', function () {
- const result = urlUtil.parseFaviconDataUrl('data:image/jpg,foo')
- assert.equal(result, null)
- })
- it('no-extension data URL', function () {
- const result = urlUtil.parseFaviconDataUrl('data:image/;base64,foo')
- assert.equal(result, null)
- })
- it('valid jpg', function () {
- const jpg = 'data:image/jpeg;base64,' +
- '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDADIiJSwlHzIsKSw4NTI7S31RS0VFS5ltc1p9tZ++u7Kf' +
- 'r6zI4f/zyNT/16yv+v/9////////wfD/////////////2wBDATU4OEtCS5NRUZP/zq/O////////' +
- '////////////////////////////////////////////////////////////wAARCAAYAEADAREA' +
- '//AhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAQMAAgQF/8QAJRABAAIBBAEEAgMAAAAAAAAAAQIR' +
- '//AAMSITEEEyJBgTORUWFx/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAA' +
- '//AAD/2gAMAwEAAhEDEQA/AOgM52xQDrjvAV5Xv0vfKUALlTQfeBm0HThMNHXkL0Lw/swN5qgA8yT4' +
- '//MCS1OEOJV8mBz9Z05yfW8iSx7p4j+jA1aD6Wj7ZMzstsfvAas4UyRHvjrAkC9KhpLMClQntlqFc2' +
- '//X1gUj4viwVObKrddH9YDoHvuujAEuNV+bLwFS8XxdSr+Cq3Vf+4F5RgQl6ZR2p1eAzU/HX80YBYy' +
- '//JLCuexwJCO2O1bwCRidAfWBSctswbI12GAJT3yiwFR7+MBjGK2g/WAJR3FdF84E2rK5VR0YH/9k='
- const expected = '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDADIiJSwlHzIsKSw4NTI7S31RS0VFS5ltc1p9tZ++u7Kf' +
- 'r6zI4f/zyNT/16yv+v/9////////wfD/////////////2wBDATU4OEtCS5NRUZP/zq/O////////' +
- '////////////////////////////////////////////////////////////wAARCAAYAEADAREA' +
- '//AhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAQMAAgQF/8QAJRABAAIBBAEEAgMAAAAAAAAAAQIR' +
- '//AAMSITEEEyJBgTORUWFx/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAA' +
- '//AAD/2gAMAwEAAhEDEQA/AOgM52xQDrjvAV5Xv0vfKUALlTQfeBm0HThMNHXkL0Lw/swN5qgA8yT4' +
- '//MCS1OEOJV8mBz9Z05yfW8iSx7p4j+jA1aD6Wj7ZMzstsfvAas4UyRHvjrAkC9KhpLMClQntlqFc2' +
- '//X1gUj4viwVObKrddH9YDoHvuujAEuNV+bLwFS8XxdSr+Cq3Vf+4F5RgQl6ZR2p1eAzU/HX80YBYy' +
- '//JLCuexwJCO2O1bwCRidAfWBSctswbI12GAJT3yiwFR7+MBjGK2g/WAJR3FdF84E2rK5VR0YH/9k='
- const result = urlUtil.parseFaviconDataUrl(jpg)
- assert.deepEqual(result, {data: expected, ext: 'jpeg'})
- })
- it('valid png', function () {
- const png = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU//5ErkJggg=='
- const result = urlUtil.parseFaviconDataUrl(png)
- assert.deepEqual(result, {data: 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU//5ErkJggg==', ext: 'png'})
- })
- })
-
- describe('isInternalUrl', function () {
- it('null scenario', function () {
- const result = urlUtil.isInternalUrl(null)
- assert.equal(result, false)
- })
- it('localhost', function () {
- const result = urlUtil.isInternalUrl('http://localhost:399/abc')
- assert.equal(result, true)
- })
- it('localhost.com', function () {
- const result = urlUtil.isInternalUrl('http://localhost.com:399/abc')
- assert.equal(result, false)
- })
- it('invalid URL', function () {
- const result = urlUtil.isInternalUrl('adsf')
- assert.equal(result, false)
- })
- it('local IP', function () {
- const result = urlUtil.isInternalUrl('http://192.168.1.255:3000')
- assert.equal(result, true)
- const result2 = urlUtil.isInternalUrl('http://127.0.0.1/')
- assert.equal(result2, true)
- })
- it('remote IP', function () {
- const result = urlUtil.isInternalUrl('http://54.0.0.1:3000')
- assert.equal(result, false)
- })
- it('local IPv6', function () {
- const result = urlUtil.isInternalUrl('https://[::1]:3000')
- assert.equal(result, true)
- const result2 = urlUtil.isInternalUrl('http://[fe80::c12:79e9:bd20:31e1]/')
- assert.equal(result2, true)
- })
- it('remote IPv6', function () {
- const result = urlUtil.isInternalUrl('http://[2001:4860:4860::8888]:8000')
- assert.equal(result, false)
- })
- it('.local URL', function () {
- const result = urlUtil.isInternalUrl('https://adsf.local')
- assert.equal(result, true)
- })
- })
-
- describe('isUrlPDF', function () {
- it('null case', function () {
- const result = urlUtil.isUrlPDF(null)
- assert.equal(result, false)
- })
-
- it('url is not pdf', function () {
- const result = urlUtil.isUrlPDF('https://clifton.io')
- assert.equal(result, false)
- })
-
- it('url is pdf', function () {
- const result = urlUtil.isUrlPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/http://www.test.com/test.pdf')
- assert.equal(result, true)
- })
- })
-
- describe('getUrlFromPDFUrl', function () {
- it('null case', function () {
- const result = urlUtil.getUrlFromPDFUrl(null)
- assert.equal(result, null)
- })
-
- it('url is not PDF', function () {
- const result = urlUtil.getUrlFromPDFUrl('https://clifton.io')
- assert.equal(result, 'https://clifton.io')
- })
-
- it('url is pdf', function () {
- const result = urlUtil.getUrlFromPDFUrl('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/http://www.test.com/test.pdf')
- assert.equal(result, 'http://www.test.com/test.pdf')
- })
- })
-})
+runMuonCompatibleTests('urlutil', components)
diff --git a/test/unit/lib/urlutilTestComponents.js b/test/unit/lib/urlutilTestComponents.js
new file mode 100644
index 00000000000..dc797f708b9
--- /dev/null
+++ b/test/unit/lib/urlutilTestComponents.js
@@ -0,0 +1,566 @@
+// lazy load requires for dual use in and outside muon
+const urlUtil = () => require('../../../js/lib/urlutil')
+
+module.exports = {
+ 'getScheme': {
+ 'null for empty': (test) => {
+ test.equal(urlUtil().getScheme('/file/path/to/file'), null)
+ },
+ 'localhost: for localhost': (test) => {
+ test.equal(urlUtil().getScheme('localhost://127.0.0.1'), 'localhost:')
+ },
+ 'gets scheme with :': (test) => {
+ test.equal(urlUtil().getScheme('data:datauri'), 'data:')
+ },
+ 'host:port is not recognized as a scheme': (test) => {
+ test.equal(urlUtil().getScheme('localhost:8089'), null)
+ },
+ 'gets scheme with ://': (test) => {
+ test.equal(urlUtil().getScheme('http://www.brave.com'), 'http://')
+ }
+ },
+
+ 'prependScheme': {
+ 'returns null when input is null': (test) => {
+ test.equal(urlUtil().prependScheme(null), null)
+ },
+ 'returns undefined when input is undefined': (test) => {
+ test.equal(urlUtil().prependScheme(), undefined)
+ },
+ 'prepends file:// to absolute file path': (test) => {
+ test.equal(urlUtil().prependScheme('/file/path/to/file'), 'file:///file/path/to/file')
+ },
+ 'defaults to http://': (test) => {
+ test.equal(urlUtil().prependScheme('www.brave.com'), 'http://www.brave.com')
+ },
+ 'keeps schema if already exists': (test) => {
+ test.equal(urlUtil().prependScheme('https://www.brave.com'), 'https://www.brave.com')
+ }
+ },
+
+ 'isNotURL': {
+ 'returns false when input:': {
+ 'is a valid URL': (test) => {
+ test.equal(urlUtil().isNotURL('brave.com'), false)
+ },
+ 'is an absolute file path without scheme': (test) => {
+ test.equal(urlUtil().isNotURL('/file/path/to/file'), false)
+ },
+ 'is an absolute file path with scheme': (test) => {
+ test.equal(urlUtil().isNotURL('file:///file/path/to/file'), false)
+ },
+ 'for special pages': {
+ 'is a data URI': (test) => {
+ test.equal(urlUtil().isNotURL('data:text/html,hi'), false)
+ },
+ 'is a view source URL': (test) => {
+ test.equal(urlUtil().isNotURL('view-source://url-here'), false)
+ },
+ 'is a mailto link': (test) => {
+ test.equal(urlUtil().isNotURL('mailto:brian@brave.com'), false)
+ },
+ 'is an about page': (test) => {
+ test.equal(urlUtil().isNotURL('about:preferences'), false)
+ },
+ 'is a chrome-extension page': (test) => {
+ test.equal(urlUtil().isNotURL('chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/cast_sender.js'), false)
+ },
+ 'is a magnet URL': (test) => {
+ test.equal(urlUtil().isNotURL('chrome://gpu'), false)
+ },
+ 'is a chrome page': (test) => {
+ test.equal(urlUtil().isNotURL('magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C'), false)
+ }
+ },
+ 'contains a hostname and port number': (test) => {
+ test.equal(urlUtil().isNotURL('someBraveServer:8089'), false)
+ },
+ 'starts or ends with whitespace': (test) => {
+ test.equal(urlUtil().isNotURL(' http://brave.com '), false)
+ test.equal(urlUtil().isNotURL('\n\nhttp://brave.com\n\n'), false)
+ test.equal(urlUtil().isNotURL('\t\thttp://brave.com\t\t'), false)
+ },
+ 'is a URL which contains basic auth user/pass': (test) => {
+ test.equal(urlUtil().isNotURL('http://username:password@example.com'), false)
+ },
+ 'is localhost (case-insensitive)': (test) => {
+ test.equal(urlUtil().isNotURL('LoCaLhOsT'), false)
+ },
+ 'is a hostname (not a domain)': (test) => {
+ test.equal(urlUtil().isNotURL('http://computer001/phpMyAdmin'), false)
+ },
+ 'ends with period (input contains a forward slash and domain)': (test) => {
+ test.equal(urlUtil().isNotURL('brave.com/test/cc?_ri_=3vv-8-e.'), false)
+ },
+ 'is a string with whitespace but has schema': (test) => {
+ test.equal(urlUtil().isNotURL('https://wwww.brave.com/test space.jpg'), false)
+ },
+ 'has custom protocol': (test) => {
+ test.equal(urlUtil().isNotURL('brave://test'), false)
+ }
+ },
+
+ 'returns true when input:': {
+ 'is null or undefined': (test) => {
+ test.equal(urlUtil().isNotURL(), true)
+ test.equal(urlUtil().isNotURL(null), true)
+ },
+ 'is not a string': (test) => {
+ test.equal(urlUtil().isNotURL(false), true)
+ test.equal(urlUtil().isNotURL(333.449), true)
+ },
+ 'is a quoted string': (test) => {
+ test.equal(urlUtil().isNotURL('"search term here"'), true)
+ },
+ 'is a pure string (no TLD)': (test) => {
+ test.equal(urlUtil().isNotURL('brave'), true)
+ },
+ 'search query': {
+ 'starts with question mark': (test) => {
+ test.equal(urlUtil().isNotURL('?brave'), true)
+ },
+ 'has a question mark followed by a space': (test) => {
+ test.equal(urlUtil().isNotURL('? brave'), true)
+ },
+ 'starts with period': (test) => {
+ test.equal(urlUtil().isNotURL('.brave'), true)
+ },
+ 'ends with period (input does NOT contain a forward slash)': (test) => {
+ test.equal(urlUtil().isNotURL('brave.'), true)
+ },
+ 'ends with period (input contains only a forward slash)': (test) => {
+ test.equal(urlUtil().isNotURL('brave/com/test/cc?_ri_=3vv-8-e.'), true)
+ }
+ },
+ 'is a string with schema but invalid domain name': (test) => {
+ test.equal(urlUtil().isNotURL('https://www.bra ve.com/test space.jpg'), true)
+ },
+ 'contains more than one word': (test) => {
+ test.equal(urlUtil().isNotURL('brave is cool'), true)
+ },
+ 'is not an about page / view source / data URI / mailto / etc': (test) => {
+ test.equal(urlUtil().isNotURL('not-a-chrome-extension:'), true)
+ test.equal(urlUtil().isNotURL('mailtoo:'), true)
+ },
+ 'is a URL (without protocol) which contains basic auth user/pass': (test) => {
+ test.equal(urlUtil().isNotURL('username:password@example.com'), true)
+ },
+ 'has space in schema': (test) => {
+ test.equal(urlUtil().isNotURL('https ://brave.com'), true)
+ }
+ }
+ },
+
+ 'getUrlFromInput': {
+ 'returns empty string when input is null': (test) => {
+ test.equal(urlUtil().getUrlFromInput(null), '')
+ },
+ 'returns empty string when input is undefined': (test) => {
+ test.equal(urlUtil().getUrlFromInput(), '')
+ },
+ 'calls prependScheme': (test) => {
+ test.equal(urlUtil().getUrlFromInput('/file/path/to/file'), 'file:///file/path/to/file')
+ }
+ },
+
+ 'isURL': {
+ 'returns !isNotURL': (test) => {
+ test.equal(urlUtil().isURL('brave.com'), !urlUtil().isNotURL('brave.com'))
+ test.equal(urlUtil().isURL('brave is cool'), !urlUtil().isNotURL('brave is cool'))
+ test.equal(urlUtil().isURL('mailto:brian@brave.com'), !urlUtil().isNotURL('mailto:brian@brave.com'))
+ }
+ },
+
+ 'isFileType': {
+ 'relative file': (test) => {
+ test.equal(urlUtil().isFileType('/file/abc/test.pdf', 'pdf'), true)
+ },
+ 'relative path': (test) => {
+ test.equal(urlUtil().isFileType('/file/abc/test', 'pdf'), false)
+ },
+ 'JPG URL': (test) => {
+ test.equal(urlUtil().isFileType('http://example.com/test/ABC.JPG?a=b#test', 'jpg'), true)
+ },
+ 'non-JPG URL': (test) => {
+ test.equal(urlUtil().isFileType('http://example.com/test/jpg', 'jpg'), false)
+ },
+ 'invalid URL': (test) => {
+ test.equal(urlUtil().isFileType('foo', 'jpg'), false)
+ }
+ },
+
+ 'toPDFJSLocation': {
+ 'pdf': (test) => {
+ const baseUrl = 'chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/'
+ test.equal(urlUtil().toPDFJSLocation('http://abc.com/test.pdf'), baseUrl + 'content/web/viewer.html?file=http%3A%2F%2Fabc.com%2Ftest.pdf')
+ },
+ 'non-pdf': (test) => {
+ test.equal(urlUtil().toPDFJSLocation('http://abc.com/test.pdf.txt'), 'http://abc.com/test.pdf.txt')
+ },
+ 'file url': (test) => {
+ test.equal(urlUtil().toPDFJSLocation('file://abc.com/test.pdf.txt'), 'file://abc.com/test.pdf.txt')
+ },
+ 'empty': (test) => {
+ test.equal(urlUtil().toPDFJSLocation(''), '')
+ }
+ },
+
+ 'getPDFViewerUrl': {
+ 'regular url': (test) => {
+ const baseUrl = 'chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/content/web/viewer.html?file='
+ test.equal(urlUtil().getPDFViewerUrl('http://example.com'), baseUrl + 'http%3A%2F%2Fexample.com')
+ },
+ 'file url': (test) => {
+ const baseUrl = 'chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/content/web/viewer.html?file='
+ test.equal(urlUtil().getPDFViewerUrl('file:///Users/yan/some files/test.pdf'), baseUrl + 'file%3A%2F%2F%2FUsers%2Fyan%2Fsome%20files%2Ftest.pdf')
+ }
+ },
+
+ 'getHostname': {
+ 'returns undefined if the URL is invalid': (test) => {
+ test.equal(urlUtil().getHostname(null), undefined)
+ },
+ 'returns the host field (including port number)': (test) => {
+ test.equal(urlUtil().getHostname('https://brave.com:8080/test/'), 'brave.com:8080')
+ },
+ 'allows you to exclude the port number': (test) => {
+ test.equal(urlUtil().getHostname('https://brave.com:8080/test/', true), 'brave.com')
+ }
+ },
+
+ 'getHostnamePatterns': {
+ 'gets bare domain hostname patterns': (test) => {
+ // XXX: *.com probably should be excluded
+ test.deepEqual(urlUtil().getHostnamePatterns('http://brave.com'),
+ ['brave.com', '*.com', 'brave.*'])
+ },
+ 'gets subdomain hostname patterns': (test) => {
+ test.deepEqual(urlUtil().getHostnamePatterns('https://bar.brave.com'),
+ ['bar.brave.com',
+ '*.brave.com',
+ 'bar.*.com',
+ 'bar.brave.*'])
+ test.deepEqual(urlUtil().getHostnamePatterns('https://foo.bar.brave.com'),
+ ['foo.bar.brave.com',
+ '*.bar.brave.com',
+ 'foo.*.brave.com',
+ 'foo.bar.*.com',
+ 'foo.bar.brave.*',
+ '*.brave.com'])
+ }
+ },
+
+ 'getLocationIfPDF': {
+ 'gets location for PDF JS URL': (test) => {
+ test.equal(urlUtil().getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf'),
+ 'https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf')
+ },
+ 'gets location for PDF JS viewer URL': (test) => {
+ test.equal(urlUtil().getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/content/web/viewer.html?file=http%3A%2F%2Funec.edu.az%2Fapplication%2Fuploads%2F2014%2F12%2Fpdf-sample.pdf'),
+ 'http://unec.edu.az/application/uploads/2014/12/pdf-sample.pdf')
+ },
+ 'does not remove wayback machine url location for PDF JS URL': (test) => {
+ test.equal(urlUtil().getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/https://web.archive.org/web/20160106152308/http://stlab.adobe.com/wiki/images/d/d3/Test.pdf'),
+ 'https://web.archive.org/web/20160106152308/http://stlab.adobe.com/wiki/images/d/d3/Test.pdf')
+ },
+ 'does not modify location for non-pdf URL': (test) => {
+ test.equal(urlUtil().getLocationIfPDF('https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf'),
+ 'https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf')
+ test.equal(urlUtil().getLocationIfPDF('chrome-extension://blank'), 'chrome-extension://blank')
+ test.equal(urlUtil().getLocationIfPDF(null), null)
+ },
+ 'gets location for file: PDF URL': (test) => {
+ let url = 'chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/file:///Users/yan/Downloads/test.pdf'
+ test.equal(urlUtil().getLocationIfPDF(url), 'file:///Users/yan/Downloads/test.pdf')
+ }
+ },
+
+ 'getDefaultFaviconUrl': {
+ 'returns empty string if input is not a URL': (test) => {
+ test.equal(urlUtil().getDefaultFaviconUrl('invalid-url-goes-here'), '')
+ },
+ 'returns the default favicon URL when given a valid URL': (test) => {
+ test.equal(urlUtil().getDefaultFaviconUrl('https://brave.com'), 'https://brave.com/favicon.ico')
+ },
+ 'includes the port in the response when given a valid URL with a port number': (test) => {
+ test.equal(urlUtil().getDefaultFaviconUrl('https://brave.com:8080'), 'https://brave.com:8080/favicon.ico')
+ }
+ },
+
+ 'getPunycodeUrl': {
+ 'returns original string if input is ASCII': (test) => {
+ test.equal(urlUtil().getPunycodeUrl('invalid-url-goes-here'), 'invalid-url-goes-here')
+ },
+ 'returns punycode ASCII string if input is non-ASCII': (test) => {
+ test.equal(urlUtil().getPunycodeUrl('ebаy.com'), 'xn--eby-7cd.com')
+ },
+ 'returns the punycode URL when given a valid URL': (test) => {
+ test.equal(urlUtil().getPunycodeUrl('http://brave:brave@ebаy.com:1234/brave#brave'), 'http://brave:brave@xn--eby-7cd.com:1234/brave#brave')
+ },
+ 'returns the punycode URL when given a URL contains @': (test) => {
+ test.equal(urlUtil().getPunycodeUrl('ebаy.com/@ebаy.com'), 'xn--eby-7cd.com/@xn--eby-7cd.com')
+ }
+ },
+
+ 'isPotentialPhishingUrl': {
+ 'returns false if input is not a URL': (test) => {
+ test.equal(urlUtil().isPotentialPhishingUrl(null), false)
+ },
+ 'returns false if input is a regular URL': (test) => {
+ test.equal(urlUtil().isPotentialPhishingUrl(' https://google.com'), false)
+ },
+ 'returns true if input is a data URL': (test) => {
+ test.equal(urlUtil().isPotentialPhishingUrl('data:text/html,'), true)
+ },
+ 'returns true if input is a blob URL': (test) => {
+ test.equal(urlUtil().isPotentialPhishingUrl(' BLOB:foo '), true)
+ }
+ },
+
+ 'isFileScheme': {
+ 'returns true when input:': {
+ 'is an absolute file path with scheme': (test) => {
+ test.equal(urlUtil().isFileScheme('file:///file/path/to/file'), true)
+ }
+ },
+ 'returns false when input:': {
+ 'is an absolute file path without scheme': (test) => {
+ test.equal(urlUtil().isFileScheme('/file/path/to/file'), false)
+ },
+ 'is a URL': (test) => {
+ test.equal(urlUtil().isFileScheme('http://brave.com'), false)
+ },
+ 'has custom protocol': (test) => {
+ test.equal(urlUtil().isFileScheme('brave://test'), false)
+ }
+ }
+ },
+
+ 'getDisplayHost': {
+ 'url is http': (test) => {
+ const result = urlUtil().getDisplayHost('http://brave.com')
+ test.equal(result, 'brave.com')
+ },
+
+ 'url is https': (test) => {
+ const result = urlUtil().getDisplayHost('https://brave.com')
+ test.equal(result, 'brave.com')
+ },
+
+ 'url is file': (test) => {
+ const result = urlUtil().getDisplayHost('file://brave.text')
+ test.equal(result, 'file://brave.text')
+ }
+ },
+
+ 'getOrigin': {
+ 'returns file:/// for any file url': (test) => {
+ test.strictEqual(urlUtil().getOrigin('file://'), 'file:///')
+ test.strictEqual(urlUtil().getOrigin('file:///'), 'file:///')
+ test.strictEqual(urlUtil().getOrigin('file:///some'), 'file:///')
+ test.strictEqual(urlUtil().getOrigin('file:///some/'), 'file:///')
+ test.strictEqual(urlUtil().getOrigin('file:///some/path'), 'file:///')
+ },
+ 'gets URL origin for simple url': (test) => {
+ test.strictEqual(urlUtil().getOrigin('https://abc.bing.com'), 'https://abc.bing.com')
+ },
+ 'gets URL origin for url with port': (test) => {
+ test.strictEqual(urlUtil().getOrigin('https://bing.com:443/?test=1#abc'), 'https://bing.com:443')
+ },
+ 'gets URL origin for IP host': (test) => {
+ test.strictEqual(urlUtil().getOrigin('http://127.0.0.1:443/?test=1#abc'), 'http://127.0.0.1:443')
+ },
+ 'gets URL origin for about:': (test) => {
+ test.strictEqual(urlUtil().getOrigin('about:preferences#abc'), 'about:preferences')
+ },
+ 'gets URL origin for slashless protocol URL': (test) => {
+ test.strictEqual(urlUtil().getOrigin('about:test/foo'), 'about:test')
+ },
+ 'returns null for invalid URL': (test) => {
+ test.strictEqual(urlUtil().getOrigin('abc'), null)
+ },
+ 'returns null for empty URL': (test) => {
+ test.strictEqual(urlUtil().getOrigin(''), null)
+ },
+ 'returns null for null URL': (test) => {
+ test.strictEqual(urlUtil().getOrigin(null), null)
+ },
+ 'returns correct result for URL with hostname that is a scheme': (test) => {
+ test.strictEqual(urlUtil().getOrigin('http://http/test'), 'http://http')
+ }
+ },
+
+ 'stripLocation': {
+ 'null scenario': (test) => {
+ const result = urlUtil().stripLocation(null)
+ test.equal(result, '')
+ },
+
+ 'empty string': (test) => {
+ const result = urlUtil().stripLocation('')
+ test.equal(result, '')
+ },
+
+ 'normal url without hash or slash': (test) => {
+ const result = urlUtil().stripLocation('https://brave.com')
+ test.equal(result, 'https://brave.com')
+ },
+
+ 'normal url with hash but not at the end': (test) => {
+ const result = urlUtil().stripLocation('https://brave.com#title')
+ test.equal(result, 'https://brave.com#title')
+ },
+
+ 'normal url with hash at the end': (test) => {
+ const result = urlUtil().stripLocation('https://brave.com#')
+ test.equal(result, 'https://brave.com')
+ },
+
+ 'normal url with slash at the end': (test) => {
+ const result = urlUtil().stripLocation('https://brave.com/')
+ test.equal(result, 'https://brave.com')
+ },
+
+ 'normal url with slash hash at the end': (test) => {
+ const result = urlUtil().stripLocation('https://brave.com/#')
+ test.equal(result, 'https://brave.com')
+ },
+
+ 'normal url with white space at the end': (test) => {
+ const result = urlUtil().stripLocation('https://brave.com ')
+ test.equal(result, 'https://brave.com')
+ }
+ },
+
+ 'parseFaviconDataUrl': {
+ 'null scenario': (test) => {
+ const result = urlUtil().parseFaviconDataUrl(null)
+ test.equal(result, null)
+ },
+ 'empty string': (test) => {
+ const result = urlUtil().parseFaviconDataUrl('')
+ test.equal(result, null)
+ },
+ 'regular URL': (test) => {
+ const result = urlUtil().parseFaviconDataUrl('http://example.com')
+ test.equal(result, null)
+ },
+ 'non-image data URL': (test) => {
+ const result = urlUtil().parseFaviconDataUrl('data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678')
+ test.equal(result, null)
+ },
+ 'non-base64 data URL': (test) => {
+ const result = urlUtil().parseFaviconDataUrl('data:image/jpg,foo')
+ test.equal(result, null)
+ },
+ 'no-extension data URL': (test) => {
+ const result = urlUtil().parseFaviconDataUrl('data:image/;base64,foo')
+ test.equal(result, null)
+ },
+ 'valid jpg': (test) => {
+ const jpg = 'data:image/jpeg;base64,' +
+ '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDADIiJSwlHzIsKSw4NTI7S31RS0VFS5ltc1p9tZ++u7Kf' +
+ 'r6zI4f/zyNT/16yv+v/9////////wfD/////////////2wBDATU4OEtCS5NRUZP/zq/O////////' +
+ '////////////////////////////////////////////////////////////wAARCAAYAEADAREA' +
+ '//AhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAQMAAgQF/8QAJRABAAIBBAEEAgMAAAAAAAAAAQIR' +
+ '//AAMSITEEEyJBgTORUWFx/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAA' +
+ '//AAD/2gAMAwEAAhEDEQA/AOgM52xQDrjvAV5Xv0vfKUALlTQfeBm0HThMNHXkL0Lw/swN5qgA8yT4' +
+ '//MCS1OEOJV8mBz9Z05yfW8iSx7p4j+jA1aD6Wj7ZMzstsfvAas4UyRHvjrAkC9KhpLMClQntlqFc2' +
+ '//X1gUj4viwVObKrddH9YDoHvuujAEuNV+bLwFS8XxdSr+Cq3Vf+4F5RgQl6ZR2p1eAzU/HX80YBYy' +
+ '//JLCuexwJCO2O1bwCRidAfWBSctswbI12GAJT3yiwFR7+MBjGK2g/WAJR3FdF84E2rK5VR0YH/9k='
+ const expected = '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDADIiJSwlHzIsKSw4NTI7S31RS0VFS5ltc1p9tZ++u7Kf' +
+ 'r6zI4f/zyNT/16yv+v/9////////wfD/////////////2wBDATU4OEtCS5NRUZP/zq/O////////' +
+ '////////////////////////////////////////////////////////////wAARCAAYAEADAREA' +
+ '//AhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAQMAAgQF/8QAJRABAAIBBAEEAgMAAAAAAAAAAQIR' +
+ '//AAMSITEEEyJBgTORUWFx/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAA' +
+ '//AAD/2gAMAwEAAhEDEQA/AOgM52xQDrjvAV5Xv0vfKUALlTQfeBm0HThMNHXkL0Lw/swN5qgA8yT4' +
+ '//MCS1OEOJV8mBz9Z05yfW8iSx7p4j+jA1aD6Wj7ZMzstsfvAas4UyRHvjrAkC9KhpLMClQntlqFc2' +
+ '//X1gUj4viwVObKrddH9YDoHvuujAEuNV+bLwFS8XxdSr+Cq3Vf+4F5RgQl6ZR2p1eAzU/HX80YBYy' +
+ '//JLCuexwJCO2O1bwCRidAfWBSctswbI12GAJT3yiwFR7+MBjGK2g/WAJR3FdF84E2rK5VR0YH/9k='
+ const result = urlUtil().parseFaviconDataUrl(jpg)
+ test.deepEqual(result, {data: expected, ext: 'jpeg'})
+ },
+ 'valid png': (test) => {
+ const png = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU//5ErkJggg=='
+ const result = urlUtil().parseFaviconDataUrl(png)
+ test.deepEqual(result, {data: 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU//5ErkJggg==', ext: 'png'})
+ }
+ },
+
+ 'isInternalUrl': {
+ 'null scenario': (test) => {
+ const result = urlUtil().isInternalUrl(null)
+ test.equal(result, false)
+ },
+ 'localhost': (test) => {
+ const result = urlUtil().isInternalUrl('http://localhost:399/abc')
+ test.equal(result, true)
+ },
+ 'localhost.com': (test) => {
+ const result = urlUtil().isInternalUrl('http://localhost.com:399/abc')
+ test.equal(result, false)
+ },
+ 'invalid URL': (test) => {
+ const result = urlUtil().isInternalUrl('adsf')
+ test.equal(result, false)
+ },
+ 'local IP': (test) => {
+ const result = urlUtil().isInternalUrl('http://192.168.1.255:3000')
+ test.equal(result, true)
+ const result2 = urlUtil().isInternalUrl('http://127.0.0.1/')
+ test.equal(result2, true)
+ },
+ 'remote IP': (test) => {
+ const result = urlUtil().isInternalUrl('http://54.0.0.1:3000')
+ test.equal(result, false)
+ },
+ 'local IPv6': (test) => {
+ const result = urlUtil().isInternalUrl('https://[::1]:3000')
+ test.equal(result, true)
+ const result2 = urlUtil().isInternalUrl('http://[fe80::c12:79e9:bd20:31e1]/')
+ test.equal(result2, true)
+ },
+ 'remote IPv6': (test) => {
+ const result = urlUtil().isInternalUrl('http://[2001:4860:4860::8888]:8000')
+ test.equal(result, false)
+ },
+ '.local URL': (test) => {
+ const result = urlUtil().isInternalUrl('https://adsf.local')
+ test.equal(result, true)
+ }
+ },
+
+ 'isUrlPDF': {
+ 'null case': (test) => {
+ const result = urlUtil().isUrlPDF(null)
+ test.equal(result, false)
+ },
+
+ 'url is not pdf': (test) => {
+ const result = urlUtil().isUrlPDF('https://clifton.io')
+ test.equal(result, false)
+ },
+
+ 'url is pdf': (test) => {
+ const result = urlUtil().isUrlPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/http://www.test.com/test.pdf')
+ test.equal(result, true)
+ }
+ },
+
+ 'getUrlFromPDFUrl': {
+ 'null case': (test) => {
+ const result = urlUtil().getUrlFromPDFUrl(null)
+ test.equal(result, null)
+ },
+
+ 'url is not PDF': (test) => {
+ const result = urlUtil().getUrlFromPDFUrl('https://clifton.io')
+ test.equal(result, 'https://clifton.io')
+ },
+
+ 'url is pdf': (test) => {
+ const result = urlUtil().getUrlFromPDFUrl('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/http://www.test.com/test.pdf')
+ test.equal(result, 'http://www.test.com/test.pdf')
+ }
+ }
+}
diff --git a/test/unit/runMuonCompatibleTests.js b/test/unit/runMuonCompatibleTests.js
new file mode 100644
index 00000000000..0a257963d25
--- /dev/null
+++ b/test/unit/runMuonCompatibleTests.js
@@ -0,0 +1,31 @@
+/* global describe, it, before, after */
+
+const assert = require('assert')
+require('./braveUnit')
+
+const executeTests = (name, tests) => {
+ describe(name, function () {
+ const runnableTests = Object.keys(tests).filter((k) => typeof tests[k] === 'function')
+ if (runnableTests.length) {
+ for (let testName of runnableTests) {
+ if (testName === 'before') {
+ before(tests[testName])
+ } else if (testName === 'after') {
+ after(tests[testName])
+ } else {
+ const wrapper = tests[testName].length > 1
+ ? function (cb) { tests[testName].call(this, assert, cb) }
+ : function () { tests[testName].call(this, assert) }
+ it(testName, wrapper)
+ }
+ }
+ }
+
+ const testGroups = Object.keys(tests).filter((k) => typeof tests[k] === 'object')
+ for (let groupName of testGroups) {
+ executeTests(groupName, tests[groupName])
+ }
+ })
+}
+
+module.exports = executeTests