diff options
Diffstat (limited to 'dom/manifest/test')
26 files changed, 1841 insertions, 0 deletions
diff --git a/dom/manifest/test/browser.ini b/dom/manifest/test/browser.ini new file mode 100644 index 000000000..ad98fe26c --- /dev/null +++ b/dom/manifest/test/browser.ini @@ -0,0 +1,9 @@ +[DEFAULT] +support-files = + file_reg_appinstalled_event.html + file_testserver.sjs + manifestLoader.html + resource.sjs +[browser_ManifestFinder_browserHasManifestLink.js] +[browser_ManifestObtainer_obtain.js] +[browser_fire_appinstalled_event.js]
\ No newline at end of file diff --git a/dom/manifest/test/browser_ManifestFinder_browserHasManifestLink.js b/dom/manifest/test/browser_ManifestFinder_browserHasManifestLink.js new file mode 100644 index 000000000..5ec663962 --- /dev/null +++ b/dom/manifest/test/browser_ManifestFinder_browserHasManifestLink.js @@ -0,0 +1,84 @@ +//Used by JSHint: +/*global Cu, BrowserTestUtils, ok, add_task, gBrowser */ +"use strict"; +const { ManifestFinder } = Cu.import("resource://gre/modules/ManifestFinder.jsm", {}); +const defaultURL = new URL("http://example.org/browser/dom/manifest/test/resource.sjs"); +defaultURL.searchParams.set("Content-Type", "text/html; charset=utf-8"); + +const tests = [{ + body: ` + <link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'> + <link rel="foo bar manifest bar test" href='${defaultURL}?body={"name":"value"}'> + <link rel="manifest" href='${defaultURL}?body={"name":"fail"}'> + `, + run(result) { + ok(result, "Document has a web manifest."); + }, +}, { + body: ` + <link rel="amanifista" href='${defaultURL}?body={"name":"fail"}'> + <link rel="foo bar manifesto bar test" href='${defaultURL}?body={"name":"pass-1"}'> + <link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'>`, + run(result) { + ok(!result, "Document does not have a web manifest."); + }, +}, { + body: ` + <link rel="manifest" href=""> + <link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`, + run(result) { + ok(!result, "Manifest link is has empty href."); + }, +}, { + body: ` + <link rel="manifest"> + <link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`, + run(result) { + ok(!result, "Manifest link is missing."); + }, +}]; + +function makeTestURL({ body }) { + const url = new URL(defaultURL); + url.searchParams.set("body", encodeURIComponent(body)); + return url.href; +} + +/** + * Test basic API error conditions + */ +add_task(function*() { + const expected = "Invalid types should throw a TypeError."; + for (let invalidValue of [undefined, null, 1, {}, "test"]) { + try { + yield ManifestFinder.contentManifestLink(invalidValue); + ok(false, expected); + } catch (e) { + is(e.name, "TypeError", expected); + } + try { + yield ManifestFinder.browserManifestLink(invalidValue); + ok(false, expected); + } catch (e) { + is(e.name, "TypeError", expected); + } + } +}); + +add_task(function*() { + const runningTests = tests + .map( + test => ({ + gBrowser, + test, + url: makeTestURL(test), + }) + ) + .map( + tabOptions => BrowserTestUtils.withNewTab(tabOptions, function*(browser) { + const result = yield ManifestFinder.browserHasManifestLink(browser); + tabOptions.test.run(result); + }) + ); + yield Promise.all(runningTests); +}); diff --git a/dom/manifest/test/browser_ManifestObtainer_obtain.js b/dom/manifest/test/browser_ManifestObtainer_obtain.js new file mode 100644 index 000000000..a2e468905 --- /dev/null +++ b/dom/manifest/test/browser_ManifestObtainer_obtain.js @@ -0,0 +1,181 @@ +//Used by JSHint: +/*global ok, is, Cu, BrowserTestUtils, add_task, gBrowser, makeTestURL, requestLongerTimeout*/ +'use strict'; +const { ManifestObtainer } = Cu.import('resource://gre/modules/ManifestObtainer.jsm', {}); +const remoteURL = 'http://mochi.test:8888/browser/dom/manifest/test/resource.sjs'; +const defaultURL = new URL('http://example.org/browser/dom/manifest/test/resource.sjs'); +defaultURL.searchParams.set('Content-Type', 'text/html; charset=utf-8'); +requestLongerTimeout(4); + +const tests = [ + // Fetch tests. + { + body: ` + <link rel="manifesto" href='resource.sjs?body={"name":"fail"}'> + <link rel="foo bar manifest bar test" href='resource.sjs?body={"name":"pass-1"}'> + <link rel="manifest" href='resource.sjs?body={"name":"fail"}'>`, + run(manifest) { + is(manifest.name, 'pass-1', 'Manifest is first `link` where @rel contains token manifest.'); + } + }, { + body: ` + <link rel="foo bar manifest bar test" href='resource.sjs?body={"name":"pass-2"}'> + <link rel="manifest" href='resource.sjs?body={"name":"fail"}'> + <link rel="manifest foo bar test" href='resource.sjs?body={"name":"fail"}'>`, + run(manifest) { + is(manifest.name, 'pass-2', 'Manifest is first `link` where @rel contains token manifest.'); + }, + }, { + body: `<link rel="manifest" href='${remoteURL}?body={"name":"pass-3"}'>`, + run(err) { + is(err.name, 'TypeError', 'By default, manifest cannot load cross-origin.'); + }, + }, + // CORS Tests. + { + get body() { + const body = 'body={"name": "pass-4"}'; + const CORS = + `Access-Control-Allow-Origin=${defaultURL.origin}`; + const link = + `<link + crossorigin=anonymous + rel="manifest" + href='${remoteURL}?${body}&${CORS}'>`; + return link; + }, + run(manifest) { + is(manifest.name, 'pass-4', 'CORS enabled, manifest must be fetched.'); + }, + }, { + get body() { + const body = 'body={"name": "fail"}'; + const CORS = 'Access-Control-Allow-Origin=http://not-here'; + const link = + `<link + crossorigin + rel="manifest" + href='${remoteURL}?${body}&${CORS}'>`; + return link; + }, + run(err) { + is(err.name, 'TypeError', 'Fetch blocked by CORS - origin does not match.'); + }, + }, { + body: `<link rel="manifest" href='about:whatever'>`, + run(err) { + is(err.name, 'TypeError', 'Trying to load from about:whatever is TypeError.'); + }, + }, { + body: `<link rel="manifest" href='file://manifest'>`, + run(err) { + is(err.name, 'TypeError', 'Trying to load from file://whatever is a TypeError.'); + }, + }, + //URL parsing tests + { + body: `<link rel="manifest" href='http://[12.1212.21.21.12.21.12]'>`, + run(err) { + is(err.name, 'TypeError', 'Trying to load invalid URL is a TypeError.'); + }, + }, +]; + +function makeTestURL({ body }) { + const url = new URL(defaultURL); + url.searchParams.set('body', encodeURIComponent(body)); + return url.href; +} + +add_task(function*() { + const promises = tests + .map(test => ({ + gBrowser, + testRunner: testObtainingManifest(test), + url: makeTestURL(test) + })) + .reduce((collector, tabOpts) => { + const promise = BrowserTestUtils.withNewTab(tabOpts, tabOpts.testRunner); + collector.push(promise); + return collector; + }, []); + + const results = yield Promise.all(promises); + + function testObtainingManifest(aTest) { + return function*(aBrowser) { + try { + const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser); + aTest.run(manifest); + } catch (e) { + aTest.run(e); + } + }; + } +}); + +/* + * e10s race condition tests + * Open a bunch of tabs and load manifests + * in each tab. They should all return pass. + */ +add_task(function*() { + const defaultPath = '/browser/dom/manifest/test/manifestLoader.html'; + const tabURLs = [ + `http://example.com:80${defaultPath}`, + `http://example.org:80${defaultPath}`, + `http://example.org:8000${defaultPath}`, + `http://mochi.test:8888${defaultPath}`, + `http://sub1.test1.example.com:80${defaultPath}`, + `http://sub1.test1.example.org:80${defaultPath}`, + `http://sub1.test1.example.org:8000${defaultPath}`, + `http://sub1.test1.mochi.test:8888${defaultPath}`, + `http://sub1.test2.example.com:80${defaultPath}`, + `http://sub1.test2.example.org:80${defaultPath}`, + `http://sub1.test2.example.org:8000${defaultPath}`, + `http://sub2.test1.example.com:80${defaultPath}`, + `http://sub2.test1.example.org:80${defaultPath}`, + `http://sub2.test1.example.org:8000${defaultPath}`, + `http://sub2.test2.example.com:80${defaultPath}`, + `http://sub2.test2.example.org:80${defaultPath}`, + `http://sub2.test2.example.org:8000${defaultPath}`, + `http://sub2.xn--lt-uia.mochi.test:8888${defaultPath}`, + `http://test1.example.com:80${defaultPath}`, + `http://test1.example.org:80${defaultPath}`, + `http://test1.example.org:8000${defaultPath}`, + `http://test1.mochi.test:8888${defaultPath}`, + `http://test2.example.com:80${defaultPath}`, + `http://test2.example.org:80${defaultPath}`, + `http://test2.example.org:8000${defaultPath}`, + `http://test2.mochi.test:8888${defaultPath}`, + `http://test:80${defaultPath}`, + `http://www.example.com:80${defaultPath}`, + ]; + // Open tabs an collect corresponding browsers + let browsers = [ + for (url of tabURLs) gBrowser.addTab(url).linkedBrowser + ]; + // Once all the pages have loaded, run a bunch of tests in "parallel". + yield Promise.all(( + for (browser of browsers) BrowserTestUtils.browserLoaded(browser) + )); + // Flood random browsers with requests. Once promises settle, check that + // responses all pass. + const results = yield Promise.all(( + for (browser of randBrowsers(browsers, 50)) ManifestObtainer.browserObtainManifest(browser) + )); + const pass = results.every(manifest => manifest.name === 'pass'); + ok(pass, 'Expect every manifest to have name equal to `pass`.'); + //cleanup + browsers + .map(browser => gBrowser.getTabForBrowser(browser)) + .forEach(tab => gBrowser.removeTab(tab)); + + //Helper generator, spits out random browsers + function* randBrowsers(aBrowsers, aMax) { + for (let i = 0; i < aMax; i++) { + const randNum = Math.round(Math.random() * (aBrowsers.length - 1)); + yield aBrowsers[randNum]; + } + } +}); diff --git a/dom/manifest/test/browser_fire_appinstalled_event.js b/dom/manifest/test/browser_fire_appinstalled_event.js new file mode 100644 index 000000000..517b120d3 --- /dev/null +++ b/dom/manifest/test/browser_fire_appinstalled_event.js @@ -0,0 +1,49 @@ +//Used by JSHint: +/*global Cu, BrowserTestUtils, ok, add_task, gBrowser */ +"use strict"; +const { PromiseMessage } = Cu.import("resource://gre/modules/PromiseMessage.jsm", {}); +const testPath = "/browser/dom/manifest/test/file_reg_appinstalled_event.html"; +const defaultURL = new URL("http://example.org/browser/dom/manifest/test/file_testserver.sjs"); +const testURL = new URL(defaultURL); +testURL.searchParams.append("file", testPath); + +// Enable window.onappinstalled, so we can fire events at it. +function enableOnAppInstalledPref() { + const ops = { + "set": [ + ["dom.manifest.onappinstalled", true], + ], + }; + return SpecialPowers.pushPrefEnv(ops); +} + +// Send a message for the even to be fired. +// This cause file_reg_install_event.html to be dynamically change. +function* theTest(aBrowser) { + aBrowser.allowEvents = true; + let waitForInstall = ContentTask.spawn(aBrowser, null, function*() { + yield ContentTaskUtils.waitForEvent(content.window, "appinstalled"); + }); + const { data: { success } } = yield PromiseMessage + .send(aBrowser.messageManager, "DOM:Manifest:FireAppInstalledEvent"); + ok(success, "message sent and received successfully."); + try { + yield waitForInstall; + ok(true, "AppInstalled event fired"); + } catch (err) { + ok(false, "AppInstalled event didn't fire: " + err.message); + } +} + +// Open a tab and run the test +add_task(function*() { + yield enableOnAppInstalledPref(); + let tabOptions = { + gBrowser: gBrowser, + url: testURL.href, + }; + yield BrowserTestUtils.withNewTab( + tabOptions, + theTest + ); +}); diff --git a/dom/manifest/test/common.js b/dom/manifest/test/common.js new file mode 100644 index 000000000..4f618be80 --- /dev/null +++ b/dom/manifest/test/common.js @@ -0,0 +1,22 @@ +/** + * Common infrastructure for manifest tests. + **/ +/*globals SpecialPowers, ManifestProcessor*/ +'use strict'; +const { + ManifestProcessor +} = SpecialPowers.Cu.import('resource://gre/modules/ManifestProcessor.jsm'); +const processor = ManifestProcessor; +const manifestURL = new URL(document.location.origin + '/manifest.json'); +const docURL = document.location; +const seperators = '\u2028\u2029\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000'; +const lineTerminators = '\u000D\u000A\u2028\u2029'; +const whiteSpace = `${seperators}${lineTerminators}`; +const typeTests = [1, null, {}, + [], false +]; +const data = { + jsonText: '{}', + manifestURL: manifestURL, + docURL: docURL +}; diff --git a/dom/manifest/test/file_reg_appinstalled_event.html b/dom/manifest/test/file_reg_appinstalled_event.html new file mode 100644 index 000000000..80ff15e11 --- /dev/null +++ b/dom/manifest/test/file_reg_appinstalled_event.html @@ -0,0 +1,15 @@ +<meta charset=utf-8> +<script> +"use strict"; +window.addEventListener("appinstalled", () => { + document + .querySelector("#output") + .innerHTML = "event received!"; + // Send a custom event back to the browser + // to acknowledge that we got this + const detail = { result: true } + const ev = new CustomEvent("dom.manifest.onappinstalled", { detail }); + document.dispatchEvent(ev); +}); +</script> +<h1 id=output>waiting for event</h1> diff --git a/dom/manifest/test/file_testserver.sjs b/dom/manifest/test/file_testserver.sjs new file mode 100644 index 000000000..5229de9f9 --- /dev/null +++ b/dom/manifest/test/file_testserver.sjs @@ -0,0 +1,54 @@ +"use strict"; +Components.utils.import("resource://gre/modules/NetUtil.jsm"); +Components.utils.importGlobalProperties(["URLSearchParams"]); + +function loadHTMLFromFile(path) { + // Load the HTML to return in the response from file. + // Since it's relative to the cwd of the test runner, we start there and + // append to get to the actual path of the file. + const testHTMLFile = + Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("CurWorkD", Components.interfaces.nsILocalFile); + + const testHTMLFileStream = + Components.classes["@mozilla.org/network/file-input-stream;1"]. + createInstance(Components.interfaces.nsIFileInputStream); + + path + .split("/") + .filter(path => path) + .reduce((file, path) => { + testHTMLFile.append(path) + return testHTMLFile; + }, testHTMLFile); + testHTMLFileStream.init(testHTMLFile, -1, 0, 0); + const isAvailable = testHTMLFileStream.available(); + return NetUtil.readInputStreamToString(testHTMLFileStream, isAvailable); +} + +function handleRequest(request, response) { + const query = new URLSearchParams(request.queryString); + + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + // Deliver the CSP policy encoded in the URL + if(query.has("csp")){ + response.setHeader("Content-Security-Policy", query.get("csp"), false); + } + + // Deliver the CSPRO policy encoded in the URL + if(query.has("cspro")){ + response.setHeader("Content-Security-Policy-Report-Only", query.get("cspro"), false); + } + + // Deliver the CORS header in the URL + if(query.has("cors")){ + response.setHeader("Access-Control-Allow-Origin", query.get("cors"), false); + } + + // Send HTML to test allowed/blocked behaviors + response.setHeader("Content-Type", "text/html", false); + response.write(loadHTMLFromFile(query.get("file"))); +} diff --git a/dom/manifest/test/manifestLoader.html b/dom/manifest/test/manifestLoader.html new file mode 100644 index 000000000..e24426090 --- /dev/null +++ b/dom/manifest/test/manifestLoader.html @@ -0,0 +1,13 @@ +<!doctype html> +<meta charset=utf-8> +<!-- +Uses resource.sjs to load a Web Manifest that can be loaded cross-origin. +--> +<link rel="manifest" href='resource.sjs?body={"name":"pass"}&Access-Control-Allow-Origin=*'> +<h1>Manifest loader</h1> +<p>Uses resource.sjs to load a Web Manifest that can be loaded cross-origin. The manifest looks like this:</p> +<pre> +{ + "name":"pass" +} +</pre> diff --git a/dom/manifest/test/mochitest.ini b/dom/manifest/test/mochitest.ini new file mode 100644 index 000000000..24e3b120d --- /dev/null +++ b/dom/manifest/test/mochitest.ini @@ -0,0 +1,23 @@ +[DEFAULT] +support-files = + common.js + resource.sjs + manifestLoader.html + file_reg_appinstalled_event.html + file_testserver.sjs +[test_ImageObjectProcessor_sizes.html] +[test_ImageObjectProcessor_src.html] +[test_ImageObjectProcessor_type.html] +[test_ManifestProcessor_background_color.html] +[test_ManifestProcessor_dir.html] +[test_ManifestProcessor_display.html] +[test_ManifestProcessor_icons.html] +[test_ManifestProcessor_JSON.html] +[test_ManifestProcessor_lang.html] +[test_ManifestProcessor_name_and_short_name.html] +[test_ManifestProcessor_orientation.html] +[test_ManifestProcessor_scope.html] +[test_ManifestProcessor_start_url.html] +[test_ManifestProcessor_theme_color.html] +[test_ManifestProcessor_warnings.html] +[test_window_onappinstalled_event.html]
\ No newline at end of file diff --git a/dom/manifest/test/resource.sjs b/dom/manifest/test/resource.sjs new file mode 100644 index 000000000..ec7804d3f --- /dev/null +++ b/dom/manifest/test/resource.sjs @@ -0,0 +1,85 @@ +/* Generic responder that composes a response from + * the query string of a request. + * + * It reserves some special prop names: + * - body: get's used as the response body + * - statusCode: override the 200 OK response code + * (response text is set automatically) + * + * Any property names it doesn't know about get converted into + * HTTP headers. + * + * For example: + * http://test/resource.sjs?Content-Type=text/html&body=<h1>hello</h1>&Hello=hi + * + * Outputs: + * HTTP/1.1 200 OK + * Content-Type: text/html + * Hello: hi + * <h1>hello</h1> + */ +//global handleRequest +'use strict'; +Components.utils.importGlobalProperties(["URLSearchParams"]); +const HTTPStatus = new Map([ + [100, 'Continue'], + [101, 'Switching Protocol'], + [200, 'OK'], + [201, 'Created'], + [202, 'Accepted'], + [203, 'Non-Authoritative Information'], + [204, 'No Content'], + [205, 'Reset Content'], + [206, 'Partial Content'], + [300, 'Multiple Choice'], + [301, 'Moved Permanently'], + [302, 'Found'], + [303, 'See Other'], + [304, 'Not Modified'], + [305, 'Use Proxy'], + [306, 'unused'], + [307, 'Temporary Redirect'], + [308, 'Permanent Redirect'], + [400, 'Bad Request'], + [401, 'Unauthorized'], + [402, 'Payment Required'], + [403, 'Forbidden'], + [404, 'Not Found'], + [405, 'Method Not Allowed'], + [406, 'Not Acceptable'], + [407, 'Proxy Authentication Required'], + [408, 'Request Timeout'], + [409, 'Conflict'], + [410, 'Gone'], + [411, 'Length Required'], + [412, 'Precondition Failed'], + [413, 'Request Entity Too Large'], + [414, 'Request-URI Too Long'], + [415, 'Unsupported Media Type'], + [416, 'Requested Range Not Satisfiable'], + [417, 'Expectation Failed'], + [500, 'Internal Server Error'], + [501, 'Not Implemented'], + [502, 'Bad Gateway'], + [503, 'Service Unavailable'], + [504, 'Gateway Timeout'], + [505, 'HTTP Version Not Supported'] +]); + +function handleRequest(request, response) { + const queryMap = new URLSearchParams(request.queryString); + if (queryMap.has('statusCode')) { + let statusCode = parseInt(queryMap.get('statusCode')); + let statusText = HTTPStatus.get(statusCode); + queryMap.delete('statusCode'); + response.setStatusLine('1.1', statusCode, statusText); + } + if (queryMap.has('body')) { + let body = queryMap.get('body') || ''; + queryMap.delete('body'); + response.write(decodeURIComponent(body)); + } + for (let [key, value] of queryMap.entries()) { + response.setHeader(key, value); + } +} diff --git a/dom/manifest/test/test_ImageObjectProcessor_sizes.html b/dom/manifest/test/test_ImageObjectProcessor_sizes.html new file mode 100644 index 000000000..82a8ef991 --- /dev/null +++ b/dom/manifest/test/test_ImageObjectProcessor_sizes.html @@ -0,0 +1,95 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * Image object's sizes member + * https://w3c.github.io/manifest/#sizes-member + **/ +'use strict'; +var validSizes = [{ + test: '16x16', + expect: ['16x16'] +}, { + test: 'hello 16x16 16x16', + expect: ['16x16'] +}, { + test: '32x32 16 48x48 12', + expect: ['32x32', '48x48'] +}, { + test: `${whiteSpace}128x128${whiteSpace}512x512 8192x8192 32768x32768${whiteSpace}`, + expect: ['128x128', '512x512', '8192x8192', '32768x32768'] +}, { + test: 'any', + expect: ['any'] +}, { + test: 'Any', + expect: ['Any'] +}, { + test: '16x32', + expect: ['16x32'] +}, { + test: '17x33', + expect: ['17x33'] +}, { + test: '32x32 32x32', + expect: ['32x32'] +}, { + test: '32X32', + expect: ['32X32'] +}, { + test: 'any 32x32', + expect: ['any', '32x32'] +}]; + +var testIcon = { + icons: [{ + src: 'test', + sizes: undefined + }] +}; + +validSizes.forEach(({test, expect}) => { + testIcon.icons[0].sizes = test; + data.jsonText = JSON.stringify(testIcon); + var result = processor.process(data); + var sizes = result.icons[0].sizes; + var expected = `Expect sizes to equal ${expect.join(" ")}`; + is(sizes, expect.join(" "), expected); +}); + +var testIcon = { + icons: [{ + src: 'test', + sizes: undefined + }] +}; + +var invalidSizes = ['invalid', '', ' ', '16 x 16', '32', '21', '16xx16', '16 x x 6']; +invalidSizes.forEach((invalidSize) => { + var expected = 'Expect invalid sizes to return undefined.'; + testIcon.icons[0].sizes = invalidSize; + data.jsonText = JSON.stringify(testIcon); + var result = processor.process(data); + var sizes = result.icons[0].sizes; + is(sizes, undefined, expected); +}); + +typeTests.forEach((type) => { + var expected = `Expect non-string sizes ${typeof type} to be undefined.`; + testIcon.icons[0].sizes = type; + data.jsonText = JSON.stringify(testIcon); + var result = processor.process(data); + var sizes = result.icons[0].sizes; + is(sizes, undefined, expected); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ImageObjectProcessor_src.html b/dom/manifest/test/test_ImageObjectProcessor_src.html new file mode 100644 index 000000000..cb77af0bd --- /dev/null +++ b/dom/manifest/test/test_ImageObjectProcessor_src.html @@ -0,0 +1,106 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * Image object's src member + * https://w3c.github.io/manifest/#src-member + **/ +'use strict'; +var noSrc = { + icons: [{}, { + src: [] + }, { + src: {} + }, { + src: null + }, { + type: 'image/jpg' + }, { + sizes: '1x1,2x2' + }, { + sizes: 'any', + type: 'image/jpg' + }] +}; + +var expected = `Expect icons without a src prop to be filtered out.`; +data.jsonText = JSON.stringify(noSrc); +var result = processor.process(data); +is(result.icons.length, 0, expected); + +var invalidSrc = { + icons: [{ + src: null + }, { + src: 1 + }, { + src: [] + }, { + src: {} + }, { + src: true + }, { + src: '' + }] +}; + +var expected = `Expect icons with invalid src prop to be filtered out.`; +data.jsonText = JSON.stringify(noSrc); +var result = processor.process(data); +is(result.icons.length, 0, expected); + +var expected = `Expect icon's src to be a string.`; +var withSrc = { + icons: [{ + src: 'pass' + }] +}; +data.jsonText = JSON.stringify(withSrc); +var result = processor.process(data); +is(typeof result.icons[0].src, "string", expected); + +var expected = `Expect only icons with a src prop to be kept.`; +var withSrc = { + icons: [{ + src: 'pass' + }, { + src: 'pass', + }, {}, { + foo: 'foo' + }] +}; +data.jsonText = JSON.stringify(withSrc); +var result = processor.process(data); +is(result.icons.length, 2, expected); + +var expectedURL = new URL('pass', manifestURL); +for (var icon of result.icons) { + var expected = `Expect src prop to be ${expectedURL.toString()}`; + is(icon.src.toString(), expectedURL.toString(), expected); +} + +//Resolve URLs relative to manfiest +var URLs = ['path', '/path', '../../path']; + +URLs.forEach((url) => { + var expected = `Resolve icon src URLs relative to manifest.`; + data.jsonText = JSON.stringify({ + icons: [{ + src: url + }] + }); + var absURL = new URL(url, manifestURL).toString(); + var result = processor.process(data); + is(result.icons[0].src.toString(), absURL, expected); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ImageObjectProcessor_type.html b/dom/manifest/test/test_ImageObjectProcessor_type.html new file mode 100644 index 000000000..d1b95044d --- /dev/null +++ b/dom/manifest/test/test_ImageObjectProcessor_type.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * Image object's type property + * https://w3c.github.io/manifest/#type-member + **/ + +'use strict'; +var testIcon = { + icons: [{ + src: 'test', + type: undefined + }] +}; + +var invalidMimeTypes = [ + 'application / text', + 'test;test', + ';test?test', + 'application\\text', + 'image/jpeg, image/gif' +]; +invalidMimeTypes.forEach((invalidMime) => { + var expected = `Expect invalid mime to be treated like undefined.`; + testIcon.icons[0].type = invalidMime; + data.jsonText = JSON.stringify(testIcon); + var result = processor.process(data); + is(result.icons[0].type, undefined, expected); +}); + +var validTypes = [ + 'image/jpeg', + 'IMAGE/jPeG', + `${whiteSpace}image/jpeg${whiteSpace}`, + 'image/JPEG; whatever=something', + 'image/JPEG;whatever' +]; + +validTypes.forEach((validMime) => { + var expected = `Expect valid mime to be parsed to : image/jpeg.`; + testIcon.icons[0].type = validMime; + data.jsonText = JSON.stringify(testIcon); + var result = processor.process(data); + is(result.icons[0].type, 'image/jpeg', expected); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_JSON.html b/dom/manifest/test/test_ManifestProcessor_JSON.html new file mode 100644 index 000000000..0319445eb --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_JSON.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * JSON parsing/processing tests + * https://w3c.github.io/manifest/#processing + **/ +'use strict'; +var invalidJson = ['', ` \t \n ${whiteSpace} `, '{', '{[[}']; +invalidJson.forEach((testString) => { + var expected = `Expect to recover from invalid JSON: ${testString}`; + data.jsonText = testString; + var result = processor.process(data); + SimpleTest.is(result.start_url, docURL.href, expected); +}); + +var validButUnhelpful = ["1", 1, "", "[{}]", "null"]; +validButUnhelpful.forEach((testString) => { + var expected = `Expect to recover from invalid JSON: ${testString}`; + data.jsonText = testString; + var result = processor.process(data); + SimpleTest.is(result.start_url, docURL.href, expected); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_background_color.html b/dom/manifest/test/test_ManifestProcessor_background_color.html new file mode 100644 index 000000000..e7249df4c --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_background_color.html @@ -0,0 +1,118 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1195018 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1195018</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * background_color member + * https://w3c.github.io/manifest/#background_color-member + **/ +'use strict'; + +typeTests.forEach(type => { + data.jsonText = JSON.stringify({ + background_color: type + }); + var result = processor.process(data); + + is(result.background_color, undefined, `Expect non-string background_color to be undefined: ${typeof type}.`); +}); + +var validThemeColors = [ + 'maroon', + '#f00', + '#ff0000', + 'rgb(255,0,0)', + 'rgb(255,0,0,1)', + 'rgb(255,0,0,1.0)', + 'rgb(255,0,0,100%)', + 'rgb(255 0 0)', + 'rgb(255 0 0 / 1)', + 'rgb(255 0 0 / 1.0)', + 'rgb(255 0 0 / 100%)', + 'rgb(100%, 0%, 0%)', + 'rgb(100%, 0%, 0%, 1)', + 'rgb(100%, 0%, 0%, 1.0)', + 'rgb(100%, 0%, 0%, 100%)', + 'rgb(100% 0% 0%)', + 'rgb(100% 0% 0% / 1)', + 'rgb(100%, 0%, 0%, 1.0)', + 'rgb(100%, 0%, 0%, 100%)', + 'rgb(300,0,0)', + 'rgb(300 0 0)', + 'rgb(255,-10,0)', + 'rgb(110%, 0%, 0%)', + 'rgba(255,0,0)', + 'rgba(255,0,0,1)', + 'rgba(255 0 0 / 1)', + 'rgba(100%,0%,0%,1)', + 'rgba(0,0,255,0.5)', + 'rgba(100%, 50%, 0%, 0.1)', + 'hsl(120, 100%, 50%)', + 'hsl(120 100% 50%)', + 'hsl(120, 100%, 50%, 1.0)', + 'hsl(120 100% 50% / 1.0)', + 'hsla(120, 100%, 50%)', + 'hsla(120 100% 50%)', + 'hsla(120, 100%, 50%, 1.0)', + 'hsla(120 100% 50% / 1.0)', + 'hsl(120deg, 100%, 50%)', + 'hsl(133.33333333grad, 100%, 50%)', + 'hsl(2.0943951024rad, 100%, 50%)', + 'hsl(0.3333333333turn, 100%, 50%)', +]; + +validThemeColors.forEach(background_color => { + data.jsonText = JSON.stringify({ + background_color: background_color + }); + var result = processor.process(data); + + is(result.background_color, background_color, `Expect background_color to be returned: ${background_color}.`); +}); + +var invalidThemeColors = [ + 'marooon', + 'f000000', + '#ff00000', + 'rgb(100, 0%, 0%)', + 'rgb(255,0)', + 'rbg(255,-10,0)', + 'rgb(110, 0%, 0%)', + '(255,0,0) }', + 'rgba(255)', + ' rgb(100%,0%,0%) }', + 'hsl(120, 100%, 50)', + 'hsl(120, 100%, 50.0)', + 'hsl 120, 100%, 50%', + 'hsla{120, 100%, 50%, 1}', +] + +invalidThemeColors.forEach(background_color => { + data.jsonText = JSON.stringify({ + background_color: background_color + }); + var result = processor.process(data); + + is(result.background_color, undefined, `Expect background_color to be undefined: ${background_color}.`); +}); + +// Trim tests +validThemeColors.forEach(background_color => { + var expandedThemeColor = `${seperators}${lineTerminators}${background_color}${lineTerminators}${seperators}`; + data.jsonText = JSON.stringify({ + background_color: expandedThemeColor + }); + var result = processor.process(data); + + is(result.background_color, background_color, `Expect trimmed background_color to be returned.`); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_dir.html b/dom/manifest/test/test_ManifestProcessor_dir.html new file mode 100644 index 000000000..1978eeca3 --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_dir.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1258899 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1258899</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * dir member + * https://w3c.github.io/manifest/#dir-member + **/ +'use strict'; +//Type checks +typeTests.forEach((type) => { + var expected = `Expect non - string dir to default to "auto".`; + data.jsonText = JSON.stringify({ + dir: type + }); + var result = processor.process(data); + is(result.dir, 'auto', expected); +}); + +/*Test valid values*/ +var validDirs = ['ltr', 'rtl', 'auto'] +validDirs.forEach((dir) => { + var expected = `Expect dir value to be ${dir}.`; + data.jsonText = JSON.stringify({dir}); + var result = processor.process(data); + is(result.dir, dir, expected); +}); + +//trim tests +validDirs.forEach((dir) => { + var expected = `Expect trimmed dir to be returned.`; + var expandeddir = seperators + lineTerminators + dir + lineTerminators + seperators; + data.jsonText = JSON.stringify({ + dir: expandeddir + }); + var result = processor.process(data); + is(result.dir, dir, expected); +}); + +//Unknown/Invalid directions +var invalidDirs = ['LTR', 'RtL', `fooo${whiteSpace}rtl`, '', 'bar baz, some value', 'ltr rtl auto', 'AuTo']; +invalidDirs.forEach((dir) => { + var expected = `Expect default dir "auto" to be returned: '${dir}'`; + data.jsonText = JSON.stringify({dir}); + var result = processor.process(data); + is(result.dir, 'auto', expected); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_display.html b/dom/manifest/test/test_ManifestProcessor_display.html new file mode 100644 index 000000000..10106465a --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_display.html @@ -0,0 +1,78 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * display member + * https://w3c.github.io/manifest/#display-member + **/ +'use strict'; +//Type checks +typeTests.forEach((type) => { + var expected = `Expect non - string display to default to "browser".`; + data.jsonText = JSON.stringify({ + display: type + }); + var result = processor.process(data); + is(result.display, 'browser', expected); +}); + +/*Test valid modes - case insensitive*/ +var validModes = [ + 'fullscreen', + 'standalone', + 'minimal-ui', + 'browser', + 'FullScreen', + 'standAlone', + 'minimal-UI', + 'BROWSER', +] +validModes.forEach((mode) => { + var expected = `Expect display mode to be ${mode.toLowerCase()}.`; + data.jsonText = JSON.stringify({ + display: mode + }); + var result = processor.process(data); + is(result.display, mode.toLowerCase(), expected); +}); + +//trim tests +validModes.forEach((display) => { + var expected = `Expect trimmed display mode to be returned.`; + var expandedDisplay = seperators + lineTerminators + display + lineTerminators + seperators; + data.jsonText = JSON.stringify({ + display: expandedDisplay + }); + var result = processor.process(data); + is(result.display, display.toLowerCase(), expected); +}); + +//Unknown modes +var invalidModes = [ + 'foo', + `fooo${whiteSpace}`, + '', + 'fullscreen,standalone', + 'standalone fullscreen', + 'FULLSCreENS', +]; + +invalidModes.forEach((invalidMode) => { + var expected = `Expect default display mode "browser" to be returned: '${invalidMode}'`; + data.jsonText = JSON.stringify({ + display: invalidMode + }); + var result = processor.process(data); + is(result.display, 'browser', expected); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_icons.html b/dom/manifest/test/test_ManifestProcessor_icons.html new file mode 100644 index 000000000..9bd3d90ec --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_icons.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * Manifest icons member + * https://w3c.github.io/manifest/#icons-member + **/ + +'use strict'; + +typeTests.forEach((type) => { + var expected = `Expect non-array icons to be empty array: ${typeof type}.`; + data.jsonText = JSON.stringify({ + icons: type + }); + var result = processor.process(data); + var y = SpecialPowers.unwrap(result.icons); + is(result.icons.length, 0, expected); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_lang.html b/dom/manifest/test/test_ManifestProcessor_lang.html new file mode 100644 index 000000000..f5e994175 --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_lang.html @@ -0,0 +1,112 @@ +<!DOCTYPE HTML> +<!-- +Bug 1143879 - Implement lang member of Web manifest +https://bugzilla.mozilla.org/show_bug.cgi?id=1143879 +--> +<meta charset="utf-8"> +<title>Test for Bug 1143879 - Implement lang member of Web manifest</title> +<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script src="common.js"></script> +<script> +/** + * lang member + * https://w3c.github.io/manifest/#lang-member + **/ +/*globals is, typeTests, data, processor, seperators, lineTerminators, todo_is*/ +'use strict'; +// Type checks: checks that only strings are accepted. +for (var type of typeTests) { + var expected = `Expect non-string to be undefined.`; + data.jsonText = JSON.stringify({ + lang: type + }); + var result = processor.process(data); + is(result.lang, undefined, expected); +} + +// Test valid language tags - derived from IANA and BCP-47 spec +// and our Intl.js implementation. +var validTags = [ + 'aa', 'ab', 'ae', 'af', 'ak', 'am', 'an', 'ar', 'as', 'av', 'ay', 'az', + 'ba', 'be', 'bg', 'bh', 'bi', 'bm', 'bn', 'bo', 'br', 'bs', 'ca', 'ce', + 'ch', 'co', 'cr', 'cs', 'cu', 'cv', 'cy', 'da', 'de', 'dv', 'dz', 'ee', + 'el', 'en', 'eo', 'es', 'et', 'eu', 'fa', 'ff', 'fi', 'fj', 'fo', 'fr', + 'fy', 'ga', 'gd', 'gl', 'gn', 'gu', 'gv', 'ha', 'he', 'hi', 'ho', 'hr', + 'ht', 'hu', 'hy', 'hz', 'ia', 'id', 'ie', 'ig', 'ik', 'in', 'io', + 'is', 'it', 'iu', 'iw', 'ja', 'ji', 'jv', 'jw', 'ka', 'kg', 'ki', 'kj', + 'kk', 'kl', 'km', 'kn', 'ko', 'kr', 'ks', 'ku', 'kv', 'kw', 'ky', 'la', + 'lb', 'lg', 'li', 'ln', 'lo', 'lt', 'lu', 'lv', 'mg', 'mh', 'mi', 'mk', + 'ml', 'mn', 'mo', 'mr', 'ms', 'mt', 'my', 'na', 'nb', 'nd', 'ne', 'ng', + 'nl', 'nn', 'no', 'nr', 'nv', 'ny', 'oc', 'oj', 'om', 'or', 'os', 'pa', + 'pi', 'pl', 'ps', 'pt', 'qu', 'rm', 'rn', 'ro', 'ru', 'rw', 'sa', 'sc', + 'sd', 'se', 'sg', 'sh', 'si', 'sk', 'sl', 'sm', 'sn', 'so', 'sq', 'sr', + 'ss', 'st', 'su', 'sv', 'sw', 'ta', 'te', 'tg', 'th', 'ti', 'tk', 'tl', + 'tn', 'to', 'tr', 'ts', 'tt', 'tw', 'ty', 'ug', 'uk', 'ur', 'uz', 've', + 'vi', 'vo', 'wa', 'wo', 'xh', 'yi', 'yo', 'za', 'zh', 'zu', 'en-US', + 'jp-JS', 'pt-PT', 'pt-BR', 'de-CH', 'de-DE-1901', 'es-419', 'sl-IT-nedis', + 'en-US-boont', 'mn-Cyrl-MN', 'x-fr-CH', 'sr-Cyrl', 'sr-Latn', + 'hy-Latn-IT-arevela', 'zh-TW', 'en-GB-boont-r-extended-sequence-x-private', + 'zh-nan-hans-bu-variant2-variant1-u-ca-chinese-t-zh-latn-x-private', + 'zh-cmn-Hans-CN', 'cmn-Hans-CN', 'zh-yue-HK', 'yue-HK', + 'de-CH-x-phonebk', 'az-Arab-x-AZE-derbend', 'x-whatever', + 'qaa-Qaaa-QM-x-southern' +]; +for (var tag of validTags) { + var expected = `Expect lang to be ${tag}.`; + data.jsonText = JSON.stringify({ + lang: tag + }); + var result = processor.process(data); + is(result.lang, tag, expected); +} + +// trim tests - check that language tags get trimmed properly. +for (var tag of validTags) { + var expected = `Expect trimmed tag to be returned.`; + var expandedtag = seperators + lineTerminators + tag; + expandedtag += lineTerminators + seperators; + data.jsonText = JSON.stringify({ + lang: expandedtag + }); + var result = processor.process(data); + is(result.lang, tag, expected); +} + +//Invalid language tags, derived from BCP-47 and made up. +var invalidTags = [ + 'de-419-DE', ' a-DE ', 'ar-a-aaa-b-bbb-a-ccc', 'sdafsdfaadsfdsf', 'i', + 'i-phone', 'en US', 'EN-*-US-JP', 'JA-INVALID-TAG', '123123123' +]; +for (var item of invalidTags) { + var expected = `Expect invalid tag (${item}) to be treated as undefined.`; + data.jsonText = JSON.stringify({ + lang: item + }); + var result = processor.process(data); + todo_is(result.lang, undefined, expected); +} + +// Canonical form conversion tests. We convert the following tags, which are in +// canonical form, to upper case and expect the processor to return them +// in canonical form. +var canonicalTags = [ + 'jp-JS', 'pt-PT', 'pt-BR', 'de-CH', 'de-DE-1901', 'es-419', 'sl-IT-nedis', + 'en-US-boont', 'mn-Cyrl-MN', 'x-fr-CH', 'sr-Cyrl', 'sr-Latn', + 'hy-Latn-IT-arevela', 'zh-TW', 'en-GB-boont-r-extended-sequence-x-private', + 'zh-cmn-Hans-CN', 'cmn-Hans-CN', 'zh-yue-HK', 'yue-HK', + 'de-CH-x-phonebk', 'az-Arab-x-AZE-derbend', 'x-whatever', + 'qaa-Qaaa-QM-x-southern' +]; + +for (var tag of canonicalTags) { + var uppedTag = tag.toUpperCase(); + var expected = `Expect tag (${uppedTag}) to be in canonical form (${tag}).`; + data.jsonText = JSON.stringify({ + lang: uppedTag + }); + var result = processor.process(data); + todo_is(result.lang, tag, expected); +} + +</script> diff --git a/dom/manifest/test/test_ManifestProcessor_name_and_short_name.html b/dom/manifest/test/test_ManifestProcessor_name_and_short_name.html new file mode 100644 index 000000000..682c8d225 --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_name_and_short_name.html @@ -0,0 +1,79 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * name and short_name members + * https://w3c.github.io/manifest/#name-member + * https://w3c.github.io/manifest/#short_name-member + **/ + +'use strict'; + +var trimNamesTests = [ + `${seperators}pass${seperators}`, + `${lineTerminators}pass${lineTerminators}`, + `${whiteSpace}pass${whiteSpace}`, + //BOM + `\uFEFFpass\uFEFF` +]; +var props = ['name', 'short_name']; + +props.forEach((prop) => { + trimNamesTests.forEach((trimmableString) => { + var assetion = `Expecting ${prop} to be trimmed.`; + var obj = {}; + obj[prop] = trimmableString; + data.jsonText = JSON.stringify(obj); + var result = processor.process(data); + is(result[prop], 'pass', assetion); + }); +}); + +/* + * If the object is not a string, it becomes undefined + */ +props.forEach((prop) => { + typeTests.forEach((type) => { + var expected = `Expect non - string ${prop} to be undefined: ${typeof type}`; + var obj = {}; + obj[prop] = type; + data.jsonText = JSON.stringify(obj); + var result = processor.process(data); + SimpleTest.ok(result[prop] === undefined, true, expected); + }); +}); + +/** + * acceptable names - including long names + */ +var acceptableNames = [ + 'pass', + `pass pass pass pass pass pass pass pass pass pass pass pass pass pass + pass pass pass pass pass pass pass pass pass pass pass pass pass pass + pass pass pass pass pass pass pass pass pass pass pass pass pass pass + pass pass pass pass pass pass pass pass pass pass pass pass`, + 'これは許容できる名前です', + 'ນີ້ແມ່ນຊື່ທີ່ຍອມຮັບໄດ້' +]; + +props.forEach((prop) => { + acceptableNames.forEach((name) => { + var expected = `Expecting name to be acceptable : ${name}`; + var obj = {}; + obj[prop] = name; + data.jsonText = JSON.stringify(obj); + var result = processor.process(data); + is(result[prop], name, expected); + }); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_orientation.html b/dom/manifest/test/test_ManifestProcessor_orientation.html new file mode 100644 index 000000000..67f19a9ff --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_orientation.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * orientation member + * https://w3c.github.io/manifest/#orientation-member + **/ +'use strict'; + +typeTests.forEach((type) => { + var expected = `Expect non-string orientation to be empty string : ${typeof type}.`; + data.jsonText = JSON.stringify({ + orientation: type + }); + var result = processor.process(data); + is(result.orientation, undefined, expected); +}); + +var validOrientations = [ + 'any', + 'natural', + 'landscape', + 'portrait', + 'portrait-primary', + 'portrait-secondary', + 'landscape-primary', + 'landscape-secondary', + 'aNy', + 'NaTuRal', + 'LANDsCAPE', + 'PORTRAIT', + 'portrait-PRIMARY', + 'portrait-SECONDARY', + 'LANDSCAPE-primary', + 'LANDSCAPE-secondary', +]; + +validOrientations.forEach((orientation) => { + var expected = `Expect orientation to be returned: ${orientation}.`; + data.jsonText = JSON.stringify({ orientation }); + var result = processor.process(data); + is(result.orientation, orientation.toLowerCase(), expected); +}); + +var invalidOrientations = [ + 'all', + 'ANYMany', + 'NaTuRalle', + 'portrait-primary portrait-secondary', + 'portrait-primary,portrait-secondary', + 'any-natural', + 'portrait-landscape', + 'primary-portrait', + 'secondary-portrait', + 'landscape-landscape', + 'secondary-primary' +]; + +invalidOrientations.forEach((orientation) => { + var expected = `Expect orientation to be empty string: ${orientation}.`; + data.jsonText = JSON.stringify({ orientation }); + var result = processor.process(data); + is(result.orientation, undefined, expected); +}); + +//Trim tests +validOrientations.forEach((orientation) => { + var expected = `Expect trimmed orientation to be returned.`; + var expandedOrientation = `${seperators}${lineTerminators}${orientation}${lineTerminators}${seperators}`; + data.jsonText = JSON.stringify({ + orientation: expandedOrientation + }); + var result = processor.process(data); + is(result.orientation, orientation.toLowerCase(), expected); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_scope.html b/dom/manifest/test/test_ManifestProcessor_scope.html new file mode 100644 index 000000000..b1cc9dbd1 --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_scope.html @@ -0,0 +1,89 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * Manifest scope + * https://w3c.github.io/manifest/#scope-member + **/ +'use strict'; +var expected = 'Expect non-string scope to be undefined'; +typeTests.forEach((type) => { + data.jsonText = JSON.stringify({ + scope: type + }); + var result = processor.process(data); + is(result.scope, undefined, expected); +}); + +var expected = 'Expect different origin to be treated as undefined'; +data.jsonText = JSON.stringify({ + scope: 'http://not-same-origin' +}); +var result = processor.process(data); +is(result.scope, undefined, expected); + +var expected = 'Expect the empty string to be treated as undefined.'; +data.jsonText = JSON.stringify({ + scope: '' +}); +var result = processor.process(data); +is(result.scope, undefined, expected); + +var expected = 'Resolve URLs relative to manifest.'; +var URLs = ['path', '/path', '../../path']; +URLs.forEach((url) => { + data.jsonText = JSON.stringify({ + scope: url, + start_url: "/path" + }); + var absURL = new URL(url, manifestURL).toString(); + var result = processor.process(data); + is(result.scope, absURL, expected); +}); + +var expected = 'If start URL is not in scope, return undefined.'; +data.jsonText = JSON.stringify({ + scope: 'foo', + start_url: 'bar' +}); +var result = processor.process(data); +is(result.scope, undefined, expected); + +var expected = 'If start URL is in scope, use the scope.'; +data.jsonText = JSON.stringify({ + start_url: 'foobar', + scope: 'foo' +}); +var result = processor.process(data); +is(result.scope.toString(), new URL('foo', manifestURL).toString(), expected); + +var expected = 'Expect start_url to be ' + new URL('foobar', manifestURL).toString(); +is(result.start_url.toString(), new URL('foobar', manifestURL).toString(), expected); + +var expected = 'If start URL is in scope, use the scope.'; +data.jsonText = JSON.stringify({ + start_url: '/foo/', + scope: '/foo/' +}); +var result = processor.process(data); +is(result.scope.toString(), new URL('/foo/', manifestURL).toString(), expected); + +var expected = 'If start URL is in scope, use the scope.'; +data.jsonText = JSON.stringify({ + start_url: '.././foo/', + scope: '../foo/' +}); +var result = processor.process(data); +is(result.scope.toString(), new URL('/foo/', manifestURL).toString(), expected); + + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_start_url.html b/dom/manifest/test/test_ManifestProcessor_start_url.html new file mode 100644 index 000000000..d0b381fa2 --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_start_url.html @@ -0,0 +1,59 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1079453 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1079453</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * Manifest start_url + * https://w3c.github.io/manifest/#start_url-member + **/ +'use strict'; +typeTests.forEach((type) => { + var expected = `Expect non - string start_url to be doc's url: ${typeof type}.`; + data.jsonText = JSON.stringify({ + start_url: type + }); + var result = processor.process(data); + is(result.start_url.toString(), docURL.toString(), expected); +}); + +//Not same origin +var expected = `Expect different origin URLs to become document's URL.`; +data.jsonText = JSON.stringify({ + start_url: 'http://not-same-origin' +}); +var result = processor.process(data); +is(result.start_url.toString(), docURL.toString(), expected); + +//Empty string test +var expected = `Expect empty string for start_url to become document's URL.`; +data.jsonText = JSON.stringify({ + start_url: '' +}); +var result = processor.process(data); +is(result.start_url.toString(), docURL.toString(), expected); + +//Resolve URLs relative to manfiest +var URLs = ['path', '/path', '../../path', + `${whiteSpace}path${whiteSpace}`, + `${whiteSpace}/path`, + `${whiteSpace}../../path` +]; +URLs.forEach((url) => { + var expected = `Resolve URLs relative to manifest.`; + data.jsonText = JSON.stringify({ + start_url: url + }); + var absURL = new URL(url, manifestURL).toString(); + var result = processor.process(data); + is(result.start_url.toString(), absURL, expected); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_theme_color.html b/dom/manifest/test/test_ManifestProcessor_theme_color.html new file mode 100644 index 000000000..c08830025 --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_theme_color.html @@ -0,0 +1,118 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1195018 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1195018</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +/** + * theme_color member + * https://w3c.github.io/manifest/#theme_color-member + **/ +'use strict'; + +typeTests.forEach(type => { + data.jsonText = JSON.stringify({ + theme_color: type + }); + var result = processor.process(data); + + is(result.theme_color, undefined, `Expect non-string theme_color to be undefined: ${typeof type}.`); +}); + +var validThemeColors = [ + 'maroon', + '#f00', + '#ff0000', + 'rgb(255,0,0)', + 'rgb(255,0,0,1)', + 'rgb(255,0,0,1.0)', + 'rgb(255,0,0,100%)', + 'rgb(255 0 0)', + 'rgb(255 0 0 / 1)', + 'rgb(255 0 0 / 1.0)', + 'rgb(255 0 0 / 100%)', + 'rgb(100%, 0%, 0%)', + 'rgb(100%, 0%, 0%, 1)', + 'rgb(100%, 0%, 0%, 1.0)', + 'rgb(100%, 0%, 0%, 100%)', + 'rgb(100% 0% 0%)', + 'rgb(100% 0% 0% / 1)', + 'rgb(100%, 0%, 0%, 1.0)', + 'rgb(100%, 0%, 0%, 100%)', + 'rgb(300,0,0)', + 'rgb(300 0 0)', + 'rgb(255,-10,0)', + 'rgb(110%, 0%, 0%)', + 'rgba(255,0,0)', + 'rgba(255,0,0,1)', + 'rgba(255 0 0 / 1)', + 'rgba(100%,0%,0%,1)', + 'rgba(0,0,255,0.5)', + 'rgba(100%, 50%, 0%, 0.1)', + 'hsl(120, 100%, 50%)', + 'hsl(120 100% 50%)', + 'hsl(120, 100%, 50%, 1.0)', + 'hsl(120 100% 50% / 1.0)', + 'hsla(120, 100%, 50%)', + 'hsla(120 100% 50%)', + 'hsla(120, 100%, 50%, 1.0)', + 'hsla(120 100% 50% / 1.0)', + 'hsl(120deg, 100%, 50%)', + 'hsl(133.33333333grad, 100%, 50%)', + 'hsl(2.0943951024rad, 100%, 50%)', + 'hsl(0.3333333333turn, 100%, 50%)', +]; + +validThemeColors.forEach(theme_color => { + data.jsonText = JSON.stringify({ + theme_color: theme_color + }); + var result = processor.process(data); + + is(result.theme_color, theme_color, `Expect theme_color to be returned: ${theme_color}.`); +}); + +var invalidThemeColors = [ + 'marooon', + 'f000000', + '#ff00000', + 'rgb(100, 0%, 0%)', + 'rgb(255,0)', + 'rbg(255,-10,0)', + 'rgb(110, 0%, 0%)', + '(255,0,0) }', + 'rgba(255)', + ' rgb(100%,0%,0%) }', + 'hsl(120, 100%, 50)', + 'hsl(120, 100%, 50.0)', + 'hsl 120, 100%, 50%', + 'hsla{120, 100%, 50%, 1}', +] + +invalidThemeColors.forEach(theme_color => { + data.jsonText = JSON.stringify({ + theme_color: theme_color + }); + var result = processor.process(data); + + is(result.theme_color, undefined, `Expect theme_color to be undefined: ${theme_color}.`); +}); + +// Trim tests +validThemeColors.forEach(theme_color => { + var expandedThemeColor = `${seperators}${lineTerminators}${theme_color}${lineTerminators}${seperators}`; + data.jsonText = JSON.stringify({ + theme_color: expandedThemeColor + }); + var result = processor.process(data); + + is(result.theme_color, theme_color, `Expect trimmed theme_color to be returned.`); +}); + </script> +</head> diff --git a/dom/manifest/test/test_ManifestProcessor_warnings.html b/dom/manifest/test/test_ManifestProcessor_warnings.html new file mode 100644 index 000000000..865ef8054 --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_warnings.html @@ -0,0 +1,90 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1086997 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1086997</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script src="common.js"></script> + <script> +'use strict'; + +const { + ConsoleAPI +} = SpecialPowers.Cu.import('resource://gre/modules/Console.jsm'); + +var warning = null; + +var originalWarn = ConsoleAPI.prototype.warn; +ConsoleAPI.prototype.warn = function(aWarning) { + warning = aWarning; +}; + +[ + { + func: () => data.jsonText = JSON.stringify(1), + warning: 'Manifest should be an object.', + }, + { + func: () => data.jsonText = JSON.stringify(null), + warning: 'Manifest should be an object.', + }, + { + func: () => data.jsonText = JSON.stringify('a string'), + warning: 'Manifest should be an object.', + }, + { + func: () => data.jsonText = JSON.stringify({ + scope: 'https://www.mozilla.org', + }), + warning: 'The scope URL must be same origin as document.', + }, + { + func: () => data.jsonText = JSON.stringify({ + scope: 'foo', + start_url: 'bar', + }), + warning: 'The start URL is outside the scope, so the scope is invalid.', + }, + { + func: () => data.jsonText = JSON.stringify({ + start_url: 'https://www.mozilla.org', + }), + warning: 'The start URL must be same origin as document.', + }, + { + func: () => data.jsonText = JSON.stringify({ + start_url: 42, + }), + warning: 'Expected the manifest\u2019s start_url member to be a string.', + }, + { + func: () => data.jsonText = JSON.stringify({ + theme_color: '42', + }), + warning: 'theme_color: 42 is not a valid CSS color.', + }, + { + func: () => data.jsonText = JSON.stringify({ + background_color: '42', + }), + warning: 'background_color: 42 is not a valid CSS color.', + }, +].forEach(function(test) { + test.func(); + + processor.process(data); + + is(warning, test.warning, 'Correct warning.'); + + warning = null; + data.manifestURL = manifestURL; + data.docURL = docURL; +}); + +ConsoleAPI.prototype.warn = originalWarn; + </script> +</head> diff --git a/dom/manifest/test/test_window_onappinstalled_event.html b/dom/manifest/test/test_window_onappinstalled_event.html new file mode 100644 index 000000000..af57fbf77 --- /dev/null +++ b/dom/manifest/test/test_window_onappinstalled_event.html @@ -0,0 +1,98 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1265279 +--> + +<head> + <meta charset="utf-8"> + <title>Test for Bug 1309099 - Web Manifest: Implement window.onappinstalled</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> + <script> + "use strict"; + SimpleTest.waitForExplicitFinish(); + const finish = SimpleTest.finish.bind(SimpleTest); + enableOnAppInstalledPref() + .then(createIframe) + .then(checkImplementation) + .then(checkOnappInstalledEventFired) + .then(checkAddEventListenerFires) + .then(finish) + .catch(err => { + ok(false, err.stack); + finish(); + }); + + function enableOnAppInstalledPref() { + const ops = { + "set": [ + ["dom.manifest.onappinstalled", true], + ], + }; + return SpecialPowers.pushPrefEnv(ops); + } + + // WebIDL conditional annotations for an interface are evaluate once per + // global, so we need to create an iframe to see the effects of calling + // enableOnAppInstalledPref(). + function createIframe() { + return new Promise((resolve) => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank"; + iframe.onload = () => resolve(iframe.contentWindow); + document.body.appendChild(iframe); + }); + } + + // Check that the WebIDL is as expected. + function checkImplementation(ifrWindow) { + return new Promise((resolve, reject) => { + const hasOnAppInstalledProp = ifrWindow.hasOwnProperty("onappinstalled"); + ok(hasOnAppInstalledProp, "window has own onappinstalled property"); + + // no point in continuing + if (!hasOnAppInstalledProp) { + const err = new Error("No 'onappinstalled' IDL attribute. Aborting early."); + return reject(err); + } + is(ifrWindow.onappinstalled, null, "window install is initially set to null"); + + // Check that enumerable, configurable, and has a getter and setter. + const objDescriptor = Object.getOwnPropertyDescriptor(ifrWindow, "onappinstalled"); + ok(objDescriptor.enumerable, "is enumerable"); + ok(objDescriptor.configurable, "is configurable"); + ok(objDescriptor.hasOwnProperty("get"), "has getter"); + ok(objDescriptor.hasOwnProperty("set"), "has setter"); + resolve(ifrWindow); + }); + } + + // Checks that .onappinstalled receives an event. + function checkOnappInstalledEventFired(ifrWindow) { + const customEv = new CustomEvent("appinstalled"); + return new Promise((resolve) => { + // Test is we receive the event on `appinstalled` + ifrWindow.onappinstalled = ev => { + ifrWindow.onappinstalled = null; + is(ev, customEv, "The events should be the same event object"); + resolve(ifrWindow); + }; + ifrWindow.dispatchEvent(customEv); + }); + } + + // Checks that .addEventListener("appinstalled") receives an event. + function checkAddEventListenerFires(ifrWindow) { + const customEv = new CustomEvent("appinstalled"); + return new Promise((resolve) => { + ifrWindow.addEventListener("appinstalled", function handler(ev) { + ifrWindow.removeEventListener("appinstalled", handler); + is(ev, customEv, "The events should be the same"); + resolve(ifrWindow); + }); + ifrWindow.dispatchEvent(customEv); + }); + } + </script> +</head> |