UNPKG

chrome-devtools-frontend

Version:
472 lines (378 loc) • 21.9 kB
// Copyright 2020 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 type * as Protocol from '../../generated/protocol.js'; import {createTarget} from '../../testing/EnvironmentHelpers.js'; import {describeWithMockConnection} from '../../testing/MockConnection.js'; import * as SDK from './sdk.js'; describe('RemoteObject', () => { describe('fromLocalObject', () => { it('can handle undefined', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(undefined); assert.deepEqual(remoteObject.type, 'undefined'); assert.isUndefined(remoteObject.subtype); assert.isUndefined(remoteObject.value); assert.deepEqual(remoteObject.description, 'undefined'); assert.isUndefined(remoteObject.unserializableValue()); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.isUndefined(callArguments.value); assert.isUndefined(callArguments.unserializableValue); }); it('can handle null', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(null); assert.deepEqual(remoteObject.type, 'object'); assert.deepEqual(remoteObject.subtype, 'null'); assert.isNull(remoteObject.value); assert.deepEqual(remoteObject.description, 'null'); assert.isUndefined(remoteObject.unserializableValue()); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.isNull(callArguments.value); assert.isUndefined(callArguments.unserializableValue); }); it('can handle bigints', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(1n); assert.deepEqual(remoteObject.type, 'bigint'); assert.isUndefined(remoteObject.subtype); assert.deepEqual(remoteObject.value, 1n); assert.deepEqual(remoteObject.description, '1'); assert.deepEqual(remoteObject.unserializableValue(), '1n'); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.isUndefined(callArguments.value); assert.deepEqual(callArguments.unserializableValue, '1n'); }); it('can handle numbers', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(42); assert.deepEqual(remoteObject.type, 'number'); assert.isUndefined(remoteObject.subtype); assert.deepEqual(remoteObject.value, 42); assert.deepEqual(remoteObject.description, '42'); assert.isUndefined(remoteObject.unserializableValue()); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.deepEqual(callArguments.value, 42); assert.isUndefined(callArguments.unserializableValue); }); it('can handle strings', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject('foo string'); assert.deepEqual(remoteObject.type, 'string'); assert.isUndefined(remoteObject.subtype); assert.deepEqual(remoteObject.value, 'foo string'); assert.deepEqual(remoteObject.description, 'foo string'); assert.isUndefined(remoteObject.unserializableValue()); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.deepEqual(callArguments.value, 'foo string'); assert.isUndefined(callArguments.unserializableValue); }); it('can handle NaN', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(NaN); assert.deepEqual(remoteObject.type, 'number'); assert.isUndefined(remoteObject.subtype); // Since equality comparisons don't work for NaN, we check if it is a number and if its string // representation is the correct one. assert.deepEqual(typeof remoteObject.value, 'number'); assert.deepEqual(String(remoteObject.value), String(NaN)); assert.deepEqual(remoteObject.description, 'NaN'); assert.deepEqual(remoteObject.unserializableValue(), 'NaN'); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.isUndefined(callArguments.value); assert.deepEqual(callArguments.unserializableValue, 'NaN'); }); it('can handle Infinity', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(Infinity); assert.deepEqual(remoteObject.type, 'number'); assert.isUndefined(remoteObject.subtype); assert.deepEqual(remoteObject.value, Infinity); assert.deepEqual(remoteObject.description, 'Infinity'); assert.deepEqual(remoteObject.unserializableValue(), 'Infinity'); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.isUndefined(callArguments.value); assert.deepEqual(callArguments.unserializableValue, 'Infinity'); }); it('can handle negative Infinity', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(-Infinity); assert.deepEqual(remoteObject.type, 'number'); assert.isUndefined(remoteObject.subtype); assert.deepEqual(remoteObject.value, -Infinity); assert.deepEqual(remoteObject.description, '-Infinity'); assert.deepEqual(remoteObject.unserializableValue(), '-Infinity'); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.isUndefined(callArguments.value); assert.deepEqual(callArguments.unserializableValue, '-Infinity'); }); it('can handle negative zero', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(-0); assert.deepEqual(remoteObject.type, 'number'); assert.isUndefined(remoteObject.subtype); assert.deepEqual(remoteObject.value, -0); assert.deepEqual(remoteObject.description, '0'); assert.deepEqual(remoteObject.unserializableValue(), '-0'); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.isUndefined(callArguments.value); assert.deepEqual(callArguments.unserializableValue, '-0'); }); it('can handle an array of numbers', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject([1n, 2, NaN, -0, null, undefined]); assert.deepEqual(remoteObject.type, 'object'); assert.deepEqual(remoteObject.subtype, 'array'); assert.deepEqual(remoteObject.value, [1n, 2, NaN, -0, null, undefined]); assert.deepEqual(remoteObject.description, '[1, 2, NaN, 0, null, undefined]'); assert.isUndefined(remoteObject.unserializableValue()); assert.deepEqual(remoteObject.arrayLength(), 6); assert.isTrue(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.deepEqual(callArguments.value, [1n, 2, NaN, -0, null, undefined]); assert.isUndefined(callArguments.unserializableValue); }); it('can handle an object', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject({foo: 'bar'}); assert.deepEqual(remoteObject.type, 'object'); assert.isUndefined(remoteObject.subtype); assert.deepEqual(remoteObject.value, {foo: 'bar'}); assert.deepEqual(remoteObject.description, '{foo: "bar"}'); assert.isUndefined(remoteObject.unserializableValue()); assert.isTrue(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.deepEqual(callArguments.value, {foo: 'bar'}); assert.isUndefined(callArguments.unserializableValue); }); it('can handle a nested object', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject( {foo: 'bar', baz: {nested: 34}, another: {nested: {object: true}}}); assert.deepEqual(remoteObject.type, 'object'); assert.isUndefined(remoteObject.subtype); assert.deepEqual(remoteObject.value, {foo: 'bar', baz: {nested: 34}, another: {nested: {object: true}}}); assert.deepEqual(remoteObject.description, '{foo: "bar", baz: {nested: 34}, another: {nested: {object: true}}}'); assert.isUndefined(remoteObject.unserializableValue()); assert.isTrue(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.deepEqual(callArguments.value, {foo: 'bar', baz: {nested: 34}, another: {nested: {object: true}}}); assert.isUndefined(callArguments.unserializableValue); }); it('can handle the function arguments object', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject((function(_a, _b, _c, _d) { return arguments; })(1, 2, 3, 4)); assert.deepEqual(remoteObject.type, 'object'); assert.isUndefined(remoteObject.subtype); // We can't represent an `Arguments` object, but we can compare its structure assert.deepEqual(String(remoteObject.value), '[object Arguments]'); assert.deepEqual(Object.keys(remoteObject.value), ['0', '1', '2', '3']); assert.deepEqual(Object.values(remoteObject.value), [1, 2, 3, 4]); assert.deepEqual(remoteObject.description, '{0: 1, 1: 2, 2: 3, 3: 4}'); assert.isUndefined(remoteObject.unserializableValue()); assert.isTrue(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.deepEqual(String(callArguments.value), '[object Arguments]'); assert.deepEqual(Object.keys(callArguments.value), ['0', '1', '2', '3']); assert.deepEqual(Object.values(callArguments.value), [1, 2, 3, 4]); assert.isUndefined(callArguments.unserializableValue); }); it('can handle a function', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(function func() {}); assert.deepEqual(remoteObject.type, 'function'); assert.isUndefined(remoteObject.subtype); // We can't represent an `Function` object, but we can compare its structure assert.deepEqual(typeof remoteObject.value, 'function'); const funcStrs = [ 'function func() { }', // esbuild produces different format. 'function func() {\n }', ]; assert.deepInclude(funcStrs, String(remoteObject.value)); assert.deepInclude(funcStrs, remoteObject.description); assert.isUndefined(remoteObject.unserializableValue()); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.deepInclude(funcStrs, String(callArguments.value)); assert.isUndefined(callArguments.unserializableValue); }); it('can handle an error', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(new Error('Some error message')); assert.deepEqual(remoteObject.type, 'object'); assert.isUndefined(remoteObject.subtype); // We can't represent an `Error` object, but we can compare its structure assert.deepEqual(typeof remoteObject.value, 'object'); assert.deepEqual(String(remoteObject.value), 'Error: Some error message'); assert.deepEqual(remoteObject.description, '{}'); assert.isUndefined(remoteObject.unserializableValue()); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.deepEqual(typeof callArguments.value, 'object'); assert.deepEqual(String(callArguments.value), 'Error: Some error message'); assert.isUndefined(callArguments.unserializableValue); }); it('can handle a Date', () => { const createNewFixedTimeDate = () => { const fixedTimeDate = new Date(); fixedTimeDate.setUTCFullYear(2020); fixedTimeDate.setUTCMonth(2); fixedTimeDate.setUTCDate(10); fixedTimeDate.setUTCHours(15); fixedTimeDate.setUTCMinutes(38); fixedTimeDate.setUTCSeconds(57); fixedTimeDate.setUTCMilliseconds(345); return fixedTimeDate; }; const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(createNewFixedTimeDate()); assert.deepEqual(remoteObject.type, 'object'); assert.deepEqual(remoteObject.subtype, 'date'); assert.deepEqual(remoteObject.value, createNewFixedTimeDate()); if (remoteObject.description === undefined) { assert.fail('Description is not defined'); return; } // Since string representations of dates are timezone dependent, we can't directly compare them. // Instead, we should assume that the description represents a valid Date and then we parse that // string as-is and compare that. assert.deepEqual( Date.parse(remoteObject.description), Date.parse('Tue Mar 10 2020 15:38:57 GMT+0000 (Greenwich Mean Time)')); assert.isUndefined(remoteObject.unserializableValue()); assert.isFalse(remoteObject.hasChildren); const callArguments = SDK.RemoteObject.RemoteObject.toCallArgument(remoteObject); assert.deepEqual(callArguments.value, createNewFixedTimeDate()); assert.isUndefined(callArguments.unserializableValue); }); }); describe('isNullOrUndefined', () => { it('correctly handles undefined', () => { assert.isTrue(SDK.RemoteObject.RemoteObject.isNullOrUndefined(undefined)); }); it('correctly handles RemoteObject null value', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(null); assert.isTrue(SDK.RemoteObject.RemoteObject.isNullOrUndefined(remoteObject)); }); it('correctly handles RemoteObject undefined value', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject(undefined); assert.isTrue(SDK.RemoteObject.RemoteObject.isNullOrUndefined(remoteObject)); }); it('correctly handles RemoteObject object', () => { const remoteObject = SDK.RemoteObject.RemoteObject.fromLocalObject({x: 42, y: 21}); assert.isFalse(SDK.RemoteObject.RemoteObject.isNullOrUndefined(remoteObject)); }); }); }); describe('RemoteObjectProperty', () => { describe('isAccessorProperty', () => { it('correctly handles data properties', () => { const property = new SDK.RemoteObject.RemoteObjectProperty('x', SDK.RemoteObject.RemoteObject.fromLocalObject('some string')); assert.isFalse(property.isAccessorProperty()); }); it('correctly handles accessor properties with getters', () => { const property = new SDK.RemoteObject.RemoteObjectProperty('y', null); property.getter = SDK.RemoteObject.RemoteObject.fromLocalObject(() => {}); assert.isTrue(property.isAccessorProperty()); }); it('correctly handles accessor properties with setters', () => { const property = new SDK.RemoteObject.RemoteObjectProperty('y', null); property.setter = SDK.RemoteObject.RemoteObject.fromLocalObject(() => {}); assert.isTrue(property.isAccessorProperty()); }); }); describe('match', () => { it('correctly handles properties with a null value', () => { const property = new SDK.RemoteObject.RemoteObjectProperty('foo', null); assert.isFalse(property.match({includeNullOrUndefinedValues: false, regex: null})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: null})); assert.isFalse(property.match({includeNullOrUndefinedValues: true, regex: /bar/})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: /f.*/})); }); it('correctly handles properties with a RemoteObject null value', () => { const property = new SDK.RemoteObject.RemoteObjectProperty('bar', SDK.RemoteObject.RemoteObject.fromLocalObject(null)); assert.isFalse(property.match({includeNullOrUndefinedValues: false, regex: null})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: null})); assert.isFalse(property.match({includeNullOrUndefinedValues: true, regex: /foo/})); assert.isFalse(property.match({includeNullOrUndefinedValues: false, regex: /.*r/})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: /.*r/})); }); it('correctly handles properties with a RemoteObject undefined value', () => { const property = new SDK.RemoteObject.RemoteObjectProperty('x', SDK.RemoteObject.RemoteObject.fromLocalObject(undefined)); assert.isFalse(property.match({includeNullOrUndefinedValues: false, regex: null})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: null})); assert.isFalse(property.match({includeNullOrUndefinedValues: true, regex: /y/})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: /.*/})); }); it('correctly handles properties with a RemoteObject object value', () => { const property = new SDK.RemoteObject.RemoteObjectProperty('obj', SDK.RemoteObject.RemoteObject.fromLocalObject({x: 1})); assert.isTrue(property.match({includeNullOrUndefinedValues: false, regex: null})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: null})); assert.isFalse(property.match({includeNullOrUndefinedValues: true, regex: /^b.*/})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: /.*/})); }); it('correctly matches property values\' descriptions', () => { const property = new SDK.RemoteObject.RemoteObjectProperty( 'foo', SDK.RemoteObject.RemoteObject.fromLocalObject('this is a bar')); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: /bar/})); assert.isFalse(property.match({includeNullOrUndefinedValues: true, regex: /baz/})); }); it('correctly matches accessor properties', () => { const property = new SDK.RemoteObject.RemoteObjectProperty('foo', null); property.getter = SDK.RemoteObject.RemoteObject.fromLocalObject(() => void 0); property.setter = SDK.RemoteObject.RemoteObject.fromLocalObject(() => void 0); assert.isTrue(property.match({includeNullOrUndefinedValues: false, regex: null})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: null})); assert.isFalse(property.match({includeNullOrUndefinedValues: true, regex: /bar/})); assert.isTrue(property.match({includeNullOrUndefinedValues: true, regex: /foo/})); }); }); }); describeWithMockConnection('RemoteError', () => { let target: SDK.Target.Target; let runtimeModel: SDK.RuntimeModel.RuntimeModel; beforeEach(() => { target = createTarget(); runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel) as SDK.RuntimeModel.RuntimeModel; }); it('throws on creation for non-error subtypes', () => { const object = SDK.RemoteObject.RemoteObject.fromLocalObject({}); assert.throws(() => SDK.RemoteObject.RemoteError.objectAsError(object)); }); it('caches the exception details', async () => { const exceptionDetailsStub = sinon.stub(runtimeModel, 'getExceptionDetails').returns(Promise.resolve(undefined)); const object = new SDK.RemoteObject.RemoteObjectImpl( runtimeModel, '1' as Protocol.Runtime.RemoteObjectId, 'object', 'error', {}); const error = SDK.RemoteObject.RemoteError.objectAsError(object); assert.isUndefined(await error.exceptionDetails()); assert.isUndefined(await error.exceptionDetails()); assert.isTrue(exceptionDetailsStub.calledOnce); }); it('caches the cause', async () => { const object = new SDK.RemoteObject.RemoteObjectImpl( runtimeModel, '1' as Protocol.Runtime.RemoteObjectId, 'object', 'error', {}); const getAllPropertiesStub = sinon.stub(object, 'getAllProperties').returns(Promise.resolve({internalProperties: null, properties: null})); const error = SDK.RemoteObject.RemoteError.objectAsError(object); assert.isUndefined(await error.cause()); assert.isUndefined(await error.cause()); assert.isTrue(getAllPropertiesStub.calledOnce); }); it('returns undefined if error has no "cause" property', async () => { const object = new SDK.RemoteObject.RemoteObjectImpl( runtimeModel, '1' as Protocol.Runtime.RemoteObjectId, 'object', 'error', {}); const properties = [new SDK.RemoteObject.RemoteObjectProperty( 'message', SDK.RemoteObject.RemoteObject.fromLocalObject('the message itself'))]; sinon.stub(object, 'getAllProperties').returns(Promise.resolve({internalProperties: null, properties})); const error = SDK.RemoteObject.RemoteError.objectAsError(object); assert.isUndefined(await error.cause()); }); it('returns the value of the "cause" property', async () => { const object = new SDK.RemoteObject.RemoteObjectImpl( runtimeModel, '1' as Protocol.Runtime.RemoteObjectId, 'object', 'error', {}); const causeValue = SDK.RemoteObject.RemoteObject.fromLocalObject('a string cause'); const properties = [new SDK.RemoteObject.RemoteObjectProperty('cause', causeValue)]; sinon.stub(object, 'getAllProperties').returns(Promise.resolve({internalProperties: null, properties})); const error = SDK.RemoteObject.RemoteError.objectAsError(object); assert.strictEqual(await error.cause(), causeValue); }); });