blob: 7f1eaefa100114e567acbbe8b2d37331819e0ada [file] [log] [blame]
// Copyright 2016 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 Suite of tests for extensions-detail-view. */
cr.define('extension_error_page_tests', function() {
/** @enum {string} */
const TestNames = {
Layout: 'layout',
CodeSection: 'code section',
ErrorSelection: 'error selection',
};
/**
* @constructor
* @extends {extension_test_util.ClickMock}
* @implements {extensions.ErrorPageDelegate}
*/
function MockErrorPageDelegate() {}
MockErrorPageDelegate.prototype = {
__proto__: extension_test_util.ClickMock.prototype,
/** @override */
deleteErrors: function(extensionId, errorIds, type) {},
/** @override */
requestFileSource: function(args) {
this.requestFileSourceArgs = args;
this.requestFileSourceResolver = new PromiseResolver();
return this.requestFileSourceResolver.promise;
},
};
const suiteName = 'ExtensionErrorPageTest';
suite(suiteName, function() {
/** @type {chrome.developerPrivate.ExtensionInfo} */
let extensionData;
/** @type {extensions.ErrorPage} */
let errorPage;
/** @type {MockErrorPageDelegate} */
let mockDelegate;
const extensionId = 'a'.repeat(32);
// Common data for runtime errors.
const runtimeErrorBase = {
type: chrome.developerPrivate.ErrorType.RUNTIME,
extensionId: extensionId,
fromIncognito: false,
};
// Common data for manifest errors.
const manifestErrorBase = {
type: chrome.developerPrivate.ErrorType.MANIFEST,
extensionId: extensionId,
fromIncognito: false,
};
// Initialize an extension item before each test.
setup(function() {
PolymerTest.clearBody();
const runtimeError = Object.assign(
{
source: 'chrome-extension://' + extensionId + '/source.html',
message: 'message',
id: 1,
severity: chrome.developerPrivate.ErrorLevel.ERROR,
},
runtimeErrorBase);
extensionData = extension_test_util.createExtensionInfo({
runtimeErrors: [runtimeError],
manifestErrors: [],
});
errorPage = new extensions.ErrorPage();
mockDelegate = new MockErrorPageDelegate();
errorPage.delegate = mockDelegate;
errorPage.data = extensionData;
document.body.appendChild(errorPage);
});
test(assert(TestNames.Layout), function() {
Polymer.dom.flush();
extension_test_util.testIcons(errorPage);
const testIsVisible = extension_test_util.isVisible.bind(null, errorPage);
expectTrue(testIsVisible('#closeButton'));
expectTrue(testIsVisible('#heading'));
expectTrue(testIsVisible('#errorsList'));
let errorElements = errorPage.shadowRoot.querySelectorAll('.error-item');
expectEquals(1, errorElements.length);
let error = errorElements[0];
expectEquals(
'message', error.querySelector('.error-message').textContent.trim());
expectTrue(error.querySelector('iron-icon').icon == 'cr:error');
const manifestError = Object.assign(
{
source: 'manifest.json',
message: 'invalid key',
id: 2,
manifestKey: 'permissions',
},
manifestErrorBase);
errorPage.set('data.manifestErrors', [manifestError]);
Polymer.dom.flush();
errorElements = errorPage.shadowRoot.querySelectorAll('.error-item');
expectEquals(2, errorElements.length);
error = errorElements[0];
expectEquals(
'invalid key',
error.querySelector('.error-message').textContent.trim());
expectTrue(error.querySelector('iron-icon').icon == 'cr:warning');
mockDelegate.testClickingCalls(
error.querySelector('.icon-delete-gray button'), 'deleteErrors',
[extensionId, [manifestError.id]]);
});
test(assert(TestNames.CodeSection), function(done) {
Polymer.dom.flush();
expectTrue(!!mockDelegate.requestFileSourceArgs);
args = mockDelegate.requestFileSourceArgs;
expectEquals(extensionId, args.extensionId);
expectEquals('source.html', args.pathSuffix);
expectEquals('message', args.message);
expectTrue(!!mockDelegate.requestFileSourceResolver);
const code = {
beforeHighlight: 'foo',
highlight: 'bar',
afterHighlight: 'baz',
message: 'quu',
};
mockDelegate.requestFileSourceResolver.resolve(code);
mockDelegate.requestFileSourceResolver.promise.then(function() {
Polymer.dom.flush();
expectEquals(code, errorPage.$$('extensions-code-section').code);
done();
});
});
test(assert(TestNames.ErrorSelection), function() {
const nextRuntimeError = Object.assign(
{
source: 'chrome-extension://' + extensionId + '/other_source.html',
message: 'Other error',
id: 2,
severity: chrome.developerPrivate.ErrorLevel.ERROR,
renderProcessId: 111,
renderViewId: 222,
canInspect: true,
contextUrl: 'http://test.com',
stackTrace: [{url: 'url', lineNumber: 123, columnNumber: 321}],
},
runtimeErrorBase);
// Add a new runtime error to the end.
errorPage.push('data.runtimeErrors', nextRuntimeError);
Polymer.dom.flush();
const errorElements =
errorPage.shadowRoot.querySelectorAll('.error-item .start');
const ironCollapses =
errorPage.shadowRoot.querySelectorAll('iron-collapse');
expectEquals(2, errorElements.length);
expectEquals(2, ironCollapses.length);
// The first error should be focused by default, and we should have
// requested the source for it.
expectEquals(
extensionData.runtimeErrors[0], errorPage.getSelectedError());
expectTrue(!!mockDelegate.requestFileSourceArgs);
let args = mockDelegate.requestFileSourceArgs;
expectEquals('source.html', args.pathSuffix);
expectTrue(ironCollapses[0].opened);
expectFalse(ironCollapses[1].opened);
mockDelegate.requestFileSourceResolver.resolve(null);
mockDelegate.requestFileSourceResolver = new PromiseResolver();
mockDelegate.requestFileSourceArgs = undefined;
// Tap the second error. It should now be selected and we should request
// the source for it.
MockInteractions.tap(errorElements[1]);
expectEquals(nextRuntimeError, errorPage.getSelectedError());
expectTrue(!!mockDelegate.requestFileSourceArgs);
args = mockDelegate.requestFileSourceArgs;
expectEquals('other_source.html', args.pathSuffix);
expectTrue(ironCollapses[1].opened);
expectFalse(ironCollapses[0].opened);
expectEquals(
'Unknown',
ironCollapses[0].querySelector('.context-url').textContent.trim());
expectEquals(
nextRuntimeError.contextUrl,
ironCollapses[1].querySelector('.context-url').textContent.trim());
});
});
return {
suiteName: suiteName,
TestNames: TestNames,
};
});