Commit 8a482c48 authored by rbpotter's avatar rbpotter Committed by Commit Bot

Reland: Web UI Polymer 3: Port tests for shared JS Modules

Port tests for the shared JS resources migrated to modules in
https://crrev.com/c/1682889.

Note: Tests only run if optimize_webui is false, since these
resources only exist in this case.

Bug: 965770
Change-Id: Ic98c7fcf47149a96a21311fbc52cc8192cc60bf1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1716947
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#680605}
parent 6fc9231b
...@@ -82,6 +82,10 @@ js2gtest("browser_tests_js_webui") { ...@@ -82,6 +82,10 @@ js2gtest("browser_tests_js_webui") {
"webui_resource_async_browsertest.js", "webui_resource_async_browsertest.js",
] ]
if (!optimize_webui) {
sources += [ "js/webui_resource_module_async_browsertest.js" ]
}
extra_js_files = [ extra_js_files = [
"test_browser_proxy.js", "test_browser_proxy.js",
"settings/test_password_manager_proxy.js", "settings/test_password_manager_proxy.js",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {addSingletonGetter, addWebUIListener, removeWebUIListener, sendWithPromise, webUIListenerCallback, webUIResponse} from 'chrome://resources/js/cr.m.js';
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
/** @type {string} Name of the chrome.send() message to be used in tests. */
const CHROME_SEND_NAME = 'echoMessage';
suite('CrModuleSendWithPromiseTest', function() {
let rejectPromises = false;
function whenChromeSendCalled(name) {
return new Promise(function(resolve, reject) {
registerMessageCallback(name, null, resolve);
});
}
/** @override */
setup(function() {
// Simulate a WebUI handler that echoes back all parameters passed to it.
// Rejects the promise depending on |rejectPromises|.
whenChromeSendCalled(CHROME_SEND_NAME).then(function(args) {
var callbackId = args[0];
webUIResponse.apply(
null, [callbackId, !rejectPromises].concat(args.slice(1)));
});
});
/** @override */
teardown(function() {
rejectPromises = false;
});
test('ResponseObject', function() {
var expectedResponse = {'foo': 'bar'};
return sendWithPromise(CHROME_SEND_NAME, expectedResponse)
.then(function(response) {
assertEquals(
JSON.stringify(expectedResponse), JSON.stringify(response));
});
});
test('ResponseArray', function() {
var expectedResponse = ['foo', 'bar'];
return sendWithPromise(CHROME_SEND_NAME, expectedResponse)
.then(function(response) {
assertEquals(
JSON.stringify(expectedResponse), JSON.stringify(response));
});
});
test('ResponsePrimitive', function() {
var expectedResponse = 1234;
return sendWithPromise(CHROME_SEND_NAME, expectedResponse)
.then(function(response) {
assertEquals(expectedResponse, response);
});
});
test('ResponseVoid', function() {
return sendWithPromise(CHROME_SEND_NAME).then(function(response) {
assertEquals(undefined, response);
});
});
test('Reject', function() {
rejectPromises = true;
var expectedResponse = 1234;
return sendWithPromise(CHROME_SEND_NAME, expectedResponse)
.then(
function() {
assertNotReached('should have rejected promise');
},
function(error) {
assertEquals(expectedResponse, error);
});
});
});
suite('CrModuleAddSingletonGetterTest', function() {
test('addSingletonGetter', function() {
function Foo() {}
addSingletonGetter(Foo);
assertEquals(
'function', typeof Foo.getInstance, 'Should add get instance function');
var x = Foo.getInstance();
assertEquals('object', typeof x, 'Should successfully create an object');
assertNotEquals(null, x, 'Created object should not be null');
var y = Foo.getInstance();
assertEquals(x, y, 'Should return the same object');
delete Foo.instance_;
var z = Foo.getInstance();
assertEquals('object', typeof z, 'Should work after clearing for testing');
assertNotEquals(null, z, 'Created object should not be null');
assertNotEquals(
x, z, 'Should return a different object after clearing for testing');
});
});
suite('CrModuleWebUIListenersTest', function() {
var listener1 = null;
var listener2 = null;
/** @const {string} */
var EVENT_NAME = 'my-foo-event';
teardown(function() {
if (listener1) {
removeWebUIListener(listener1);
}
if (listener2) {
removeWebUIListener(listener2);
}
});
test('removeWebUIListener', function() {
listener1 = addWebUIListener(EVENT_NAME, function() {});
assertTrue(removeWebUIListener(listener1));
assertFalse(removeWebUIListener(listener1));
assertFalse(removeWebUIListener({
eventName: 'non-existing-event',
uid: 12345,
}));
});
test('addWebUIListener_ResponseParams', function() {
var expectedString = 'foo';
var expectedNumber = 123;
var expectedArray = [1, 2];
var expectedObject = {};
return new Promise(function(resolve, reject) {
listener1 = addWebUIListener(EVENT_NAME, function(s, n, a, o) {
assertEquals(expectedString, s);
assertEquals(expectedNumber, n);
assertEquals(expectedArray, a);
assertEquals(expectedObject, o);
resolve();
});
webUIListenerCallback(
EVENT_NAME, expectedString, expectedNumber, expectedArray,
expectedObject);
});
});
test('addWebUIListener_NoResponseParams', function() {
return new Promise(function(resolve, reject) {
listener1 = addWebUIListener(EVENT_NAME, function() {
assertEquals(0, arguments.length);
resolve();
});
webUIListenerCallback(EVENT_NAME);
});
});
test('addWebUIListener_MulitpleListeners', function() {
var resolver1 = new PromiseResolver();
var resolver2 = new PromiseResolver();
listener1 = addWebUIListener(EVENT_NAME, resolver1.resolve);
listener2 = addWebUIListener(EVENT_NAME, resolver2.resolve);
webUIListenerCallback(EVENT_NAME);
// Check that both listeners registered are invoked.
return Promise.all([resolver1.promise, resolver2.promise]);
});
});
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
suite('I18nBehaviorModuleTest', function() {
const allowedByDefault = '<a href="https://google.com">Google!</a>';
const text = 'I\'m just text, nobody should have a problem with me!';
const nonBreakingSpace = 'A\u00a0B\u00a0C'; // \u00a0 is a unicode nbsp.
suiteSetup(function() {
window.loadTimeData.data = {
'allowedByDefault': allowedByDefault,
'customAttr': '<a is="action-link">Take action!</a>',
'customTag': '<x-foo>I\'m an X, foo!</x-foo>',
'javascriptHref': '<a href="javascript:alert(1)">teh hax</a>',
'script': '<script>alert(/xss/)</scr' +
'ipt>',
'text': text,
'nonBreakingSpace': nonBreakingSpace,
};
});
test('i18n', function() {
assertEquals(text, I18nBehavior.i18n('text'));
assertEquals(nonBreakingSpace, I18nBehavior.i18n('nonBreakingSpace'));
assertThrows(function() {
I18nBehavior.i18n('customAttr');
});
assertThrows(function() {
I18nBehavior.i18n('customTag');
});
assertThrows(function() {
I18nBehavior.i18n('javascriptHref');
});
assertThrows(function() {
I18nBehavior.i18n('script');
});
});
test('i18n advanced', function() {
assertEquals(
allowedByDefault, I18nBehavior.i18nAdvanced('allowedByDefault'));
I18nBehavior.i18nAdvanced('customAttr', {
attrs: {
is: function(el, val) {
return el.tagName == 'A' && val == 'action-link';
},
},
});
I18nBehavior.i18nAdvanced('customTag', {tags: ['X-FOO']});
});
test('i18n dynamic', function() {
var locale = 'en';
assertEquals(text, I18nBehavior.i18nDynamic(locale, 'text'));
});
test('i18n exists', function() {
assertTrue(I18nBehavior.i18nExists('text'));
assertFalse(I18nBehavior.i18nExists('missingText'));
});
});
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/js/load_time_data.m.js';
suite('LoadTimeDataModuleTest', function() {
const loadTimeData = window.loadTimeData;
test('sanitizeInnerHtml', function() {
// A few tests to see that that data is being passed through. The
// sanitizeInnerHtml() function calls into parseHtmlSubset() which has its
// own tests (that don't need to be repeated here).
assertEquals(
'<a href="chrome://foo"></a>',
loadTimeData.sanitizeInnerHtml('<a href="chrome://foo"></a>'));
assertThrows(() => {
loadTimeData.sanitizeInnerHtml('<div></div>');
}, 'DIV is not supported');
assertEquals(
'<div></div>',
loadTimeData.sanitizeInnerHtml('<div></div>', {tags: ['div']}));
});
test('getStringPieces', function() {
const assertSubstitutedPieces = function(expected, var_args) {
var var_args = Array.prototype.slice.call(arguments, 1);
var pieces =
loadTimeData.getSubstitutedStringPieces.apply(loadTimeData, var_args);
assertDeepEquals(expected, pieces);
// Ensure output matches getStringF.
assertEquals(
loadTimeData.substituteString.apply(loadTimeData, var_args),
pieces.map(p => p.value).join(''));
};
assertSubstitutedPieces([{value: 'paper', arg: null}], 'paper');
assertSubstitutedPieces([{value: 'paper', arg: '$1'}], '$1', 'paper');
assertSubstitutedPieces(
[
{value: 'i think ', arg: null},
{value: 'paper mario', arg: '$1'},
{value: ' is a good game', arg: null},
],
'i think $1 is a good game', 'paper mario');
assertSubstitutedPieces(
[
{value: 'paper mario', arg: '$1'},
{value: ' costs $', arg: null},
{value: '60', arg: '$2'},
],
'$1 costs $$$2', 'paper mario', '60');
assertSubstitutedPieces(
[
{value: 'paper mario', arg: '$1'},
{value: ' costs $60', arg: null},
],
'$1 costs $$60', 'paper mario');
assertSubstitutedPieces(
[
{value: 'paper mario', arg: '$1'},
{value: ' costs\n$60 ', arg: null},
{value: 'today', arg: '$2'},
],
'$1 costs\n$$60 $2', 'paper mario', 'today');
assertSubstitutedPieces(
[
{value: '$$', arg: null},
{value: '1', arg: '$1'},
{value: '2', arg: '$2'},
{value: '1', arg: '$1'},
{value: '$$2', arg: null},
{value: '2', arg: '$2'},
{value: '$', arg: null},
{value: '1', arg: '$1'},
{value: '$', arg: null},
],
'$$$$$1$2$1$$$$2$2$$$1$$', '1', '2');
});
test('unescapedDollarSign', function() {
/** @param {string} label */
const assertSubstitutionThrows = function(label) {
assertThrows(() => {
loadTimeData.getSubstitutedStringPieces(label);
}, 'Assertion failed: Unescaped $ found in localized string.');
assertThrows(() => {
loadTimeData.substituteString(label);
}, 'Assertion failed: Unescaped $ found in localized string.');
};
assertSubstitutionThrows('$');
assertSubstitutionThrows('$1$$$a2');
assertSubstitutionThrows('$$$');
assertSubstitutionThrows('a$');
assertSubstitutionThrows('a$\n');
});
});
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {parseHtmlSubset} from 'chrome://resources/js/parse_html_subset.m.js';
suite('ParseHtmlSubsetModuleTest', function() {
function parseAndAssertThrows() {
var args = arguments;
assertThrows(function() {
parseHtmlSubset.apply(null, args);
});
}
test('text', function() {
parseHtmlSubset('');
parseHtmlSubset('abc');
parseHtmlSubset('&nbsp;');
});
test('supported tags', function() {
parseHtmlSubset('<b>bold</b>');
parseHtmlSubset('Some <b>bold</b> text');
parseHtmlSubset('Some <strong>strong</strong> text');
parseHtmlSubset('<B>bold</B>');
parseHtmlSubset('Some <B>bold</B> text');
parseHtmlSubset('Some <STRONG>strong</STRONG> text');
});
test('invalid tags', function() {
parseAndAssertThrows('<unknown_tag>x</unknown_tag>');
parseAndAssertThrows('<img>');
parseAndAssertThrows(
'<script>alert(1)<' +
'/script>');
});
test('invalid attributes', function() {
parseAndAssertThrows('<b onclick="alert(1)">x</b>');
parseAndAssertThrows('<b style="color:red">x</b>');
parseAndAssertThrows('<b foo>x</b>');
parseAndAssertThrows('<b foo=bar></b>');
});
test('valid anchors', function() {
parseHtmlSubset('<a href="https://google.com">Google</a>');
parseHtmlSubset('<a href="chrome://settings">Google</a>');
});
test('invalid anchor hrefs', function() {
parseAndAssertThrows('<a href="http://google.com">Google</a>');
parseAndAssertThrows('<a href="ftp://google.com">Google</a>');
parseAndAssertThrows('<a href="http/google.com">Google</a>');
parseAndAssertThrows('<a href="javascript:alert(1)">Google</a>');
parseAndAssertThrows(
'<a href="chrome-extension://whurblegarble">Google</a>');
});
test('invalid anchor attributes', function() {
parseAndAssertThrows('<a name=foo>Google</a>');
parseAndAssertThrows(
'<a onclick="alert(1)" href="https://google.com">Google</a>');
parseAndAssertThrows(
'<a foo="bar(1)" href="https://google.com">Google</a>');
});
test('anchor target', function() {
var df = parseHtmlSubset(
'<a href="https://google.com" target="_blank">Google</a>');
assertEquals('_blank', df.firstChild.target);
});
test('invalid target', function() {
parseAndAssertThrows('<form target="_evil">', ['form']);
parseAndAssertThrows('<iframe target="_evil">', ['iframe']);
parseAndAssertThrows(
'<a href="https://google.com" target="foo">Google</a>');
});
test('custom tags', function() {
parseHtmlSubset('yo <I>ho</i><bR>yo <EM>ho</em>', ['i', 'EM', 'Br']);
});
test('invalid custom tags', function() {
parseAndAssertThrows(
'a pirate\'s<script>lifeForMe();<' +
'/script>',
['br']);
});
test('custom attributes', function() {
const returnsTruthy = function(node, value) {
assertEquals('A', node.tagName);
assertEquals('fancy', value);
return true;
};
parseHtmlSubset(
'<a class="fancy">I\'m fancy!</a>', null, {class: returnsTruthy});
});
test('invalid custom attributes', function() {
const returnsFalsey = function() {
return false;
};
parseAndAssertThrows(
'<a class="fancy">I\'m fancy!</a>', null, {class: returnsFalsey});
parseAndAssertThrows('<a class="fancy">I\'m fancy!</a>');
});
test('on error async', function(done) {
window.called = false;
parseAndAssertThrows('<img onerror="window.called = true" src="_.png">');
parseAndAssertThrows('<img src="_.png" onerror="window.called = true">');
window.setTimeout(function() {
assertFalse(window.called);
done();
});
});
});
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
suite('PromiseResolverModuleTest', function() {
test('members read only', function() {
const resolver = new PromiseResolver;
assertThrows(function() {
resolver.promise = new Promise;
});
assertThrows(function() {
resolver.resolve = function() {};
});
assertThrows(function() {
resolver.reject = function() {};
});
});
test('resolves', function(done) {
const resolver = new PromiseResolver;
resolver.promise.then(done);
resolver.resolve();
});
test('rejects', function(done) {
const resolver = new PromiseResolver;
resolver.promise.catch(done);
resolver.reject();
});
test('is fulfilled', function() {
const promises = [];
const resolver1 = new PromiseResolver;
assertFalse(resolver1.isFulfilled);
promises.push(resolver1.promise.then(() => {
assertTrue(resolver1.isFulfilled);
}));
resolver1.resolve();
const resolver2 = new PromiseResolver;
assertFalse(resolver2.isFulfilled);
promises.push(resolver2.promise.then(arg => {
assertTrue(resolver2.isFulfilled);
assertTrue(arg);
}));
resolver2.resolve(true);
const resolver3 = new PromiseResolver;
assertFalse(resolver3.isFulfilled);
promises.push(resolver3.promise.catch(() => {
assertTrue(resolver3.isFulfilled);
}));
resolver3.reject(new Error);
// Don't allow the test to end before all promises are fulfilled.
return Promise.all(promises);
});
});
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {$, quoteString} from 'chrome://resources/js/util.m.js';
suite('UtilModuleTest', function() {
test('quote string', function() {
// Basic cases.
assertEquals('\"test\"', quoteString('"test"'));
assertEquals('\\!\\?', quoteString('!?'));
assertEquals(
'\\(\\._\\.\\) \\( \\:l \\) \\(\\.-\\.\\)',
quoteString('(._.) ( :l ) (.-.)'));
// Using the output as a regex.
var re = new RegExp(quoteString('"hello"'), 'gim');
var match = re.exec('She said "Hello" loudly');
assertEquals(9, match.index);
re = new RegExp(quoteString('Hello, .*'), 'gim');
match = re.exec('Hello, world');
assertEquals(null, match);
});
test('click handler', function() {
document.body.innerHTML = `
<a id="file" href="file:///path/to/file">File</a>
<a id="chrome" href="about:chrome">Chrome</a>
<a href="about:blank"><b id="blank">Click me</b></a>
`;
var clickArgs = null;
var oldSend = chrome.send;
chrome.send = function(message, args) {
assertEquals('navigateToUrl', message);
clickArgs = args;
};
$('file').click();
assertEquals('file:///path/to/file', clickArgs[0]);
$('chrome').click();
assertEquals('about:chrome', clickArgs[0]);
$('blank').click();
assertEquals('about:blank', clickArgs[0]);
chrome.send = oldSend;
});
});
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview JS tests for various chrome://resources JS modules.
*/
/** Test fixture for testing shared JS module resources. */
var WebUIResourceModuleAsyncTest = class extends testing.Test {
/** @override */
get browsePreload() {
return DUMMY_URL;
}
/** @override */
get isAsync() {
return true;
}
/** @override */
get runAccessibilityChecks() {
return false;
}
/** @override */
get webuiHost() {
return 'dummyurl';
}
/** @override */
get extraLibraries() {
return [
'//third_party/mocha/mocha.js',
'//chrome/test/data/webui/mocha_adapter.js',
];
}
};
var CrModuleTest = class extends WebUIResourceModuleAsyncTest {
/** @override */
get browsePreload() {
return 'chrome://test?module=js/cr_test.js';
}
};
TEST_F('CrModuleTest', 'AddSingletonGetter', function() {
mocha.fgrep('CrModuleAddSingletonGetterTest').run();
});
TEST_F('CrModuleTest', 'SendWithPromise', function() {
mocha.fgrep('CrModuleSendWithPromiseTest').run();
});
TEST_F('CrModuleTest', 'WebUIListeners', function() {
mocha.fgrep('CrModuleWebUIListenersTest').run();
});
var PromiseResolverModuleTest = class extends WebUIResourceModuleAsyncTest {
/** @override */
get browsePreload() {
return 'chrome://test?module=js/promise_resolver_test.js';
}
};
TEST_F('PromiseResolverModuleTest', 'All', function() {
mocha.run();
});
var ParseHtmlSubsetModuleTest = class extends WebUIResourceModuleAsyncTest {
/** @override */
get browsePreload() {
return 'chrome://test?module=js/parse_html_subset_test.js';
}
};
TEST_F('ParseHtmlSubsetModuleTest', 'All', function() {
mocha.run();
});
var UtilModuleTest = class extends WebUIResourceModuleAsyncTest {
/** @override */
get browsePreload() {
return 'chrome://test?module=js/util_test.js';
}
};
TEST_F('UtilModuleTest', 'All', function() {
mocha.run();
});
var LoadTimeDataModuleTest = class extends WebUIResourceModuleAsyncTest {
/** @override */
get browsePreload() {
return 'chrome://test?module=js/load_time_data_test.js';
}
};
TEST_F('LoadTimeDataModuleTest', 'All', function() {
mocha.run();
});
var I18nBehaviorModuleTest = class extends WebUIResourceModuleAsyncTest {
/** @override */
get browsePreload() {
return 'chrome://test?module=js/i18n_behavior_test.js';
}
};
TEST_F('I18nBehaviorModuleTest', 'All', function() {
mocha.run();
});
...@@ -23,9 +23,10 @@ ...@@ -23,9 +23,10 @@
} }
const error = new Error(message); const error = new Error(message);
const global = function() { const global = function() {
const thisOrSelf = this || self;
/** @type {boolean} */ /** @type {boolean} */
this.traceAssertionsForTesting; thisOrSelf.traceAssertionsForTesting;
return this; return thisOrSelf;
}(); }();
if (global.traceAssertionsForTesting) { if (global.traceAssertionsForTesting) {
console.warn(error.stack); console.warn(error.stack);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment