| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as FormatterWorker from './formatter_worker.js'; |
| import * as FormatterAction from './FormatterActions.js'; // eslint-disable-line @devtools/es-modules-import |
| |
| describe('ScopeParser', () => { |
| describe('parseScopes', () => { |
| const {parseScopes} = FormatterWorker.ScopeParser; |
| |
| it('parses simple function', () => { |
| const scopes = parseScopes('function foo(a){}'); |
| |
| const innerScope = scopes?.children[0]; |
| assert.strictEqual(innerScope?.start, 12); |
| assert.strictEqual(innerScope?.end, 17); |
| assert.strictEqual(innerScope?.kind, FormatterAction.ScopeKind.FUNCTION); |
| assert.strictEqual(innerScope?.name, 'foo'); |
| assert.deepEqual(innerScope?.nameMappingLocations, [9, 12]); |
| assert.deepEqual(innerScope?.variables?.get('a')?.uses.map(u => u.offset), [13]); |
| }); |
| |
| it('parses function expression', () => { |
| const scopes = parseScopes('const foo = function(a) {}; const bar = function b() {}'); |
| |
| const scopeFoo = scopes?.children[0]; |
| assert.strictEqual(scopeFoo?.kind, FormatterAction.ScopeKind.FUNCTION); |
| assert.isUndefined(scopeFoo?.name); |
| assert.deepEqual(scopeFoo?.nameMappingLocations, [20]); |
| |
| const scopeBar = scopes?.children[1]; |
| assert.strictEqual(scopeBar?.kind, FormatterAction.ScopeKind.FUNCTION); |
| assert.strictEqual(scopeBar?.name, 'b'); |
| assert.deepEqual(scopeBar?.nameMappingLocations, [49, 50]); |
| }); |
| |
| it('parses arrow function', () => { |
| const scopes = parseScopes('let f = (a) => {}'); |
| |
| assert.strictEqual(scopes?.children.length, 1); |
| const innerScope = scopes?.children[0]; |
| assert.strictEqual(innerScope?.start, 8); |
| assert.strictEqual(innerScope?.end, 17); |
| assert.strictEqual(innerScope?.kind, FormatterAction.ScopeKind.ARROW_FUNCTION); |
| assert.deepEqual(innerScope?.variables?.size, 1); |
| assert.deepEqual(innerScope?.variables?.get('a')?.uses.map(u => u.offset), [9]); |
| assert.deepEqual(innerScope?.nameMappingLocations, [8, 12]); |
| }); |
| |
| it('parses for loop', () => { |
| const scopes = parseScopes('for (let i = 0; i < 3; i++) console.log(i);'); |
| |
| const innerScope = scopes?.children[0]; |
| assert.strictEqual(innerScope?.start, 0); |
| assert.strictEqual(innerScope?.end, 43); |
| assert.deepEqual(innerScope?.variables?.size, 1); |
| assert.deepEqual(innerScope?.variables?.get('i')?.uses.map(u => u.offset), [9, 16, 23, 40]); |
| }); |
| |
| it('parses block scope', () => { |
| const scopes = parseScopes('let x; { let y; }'); |
| |
| assert.strictEqual(scopes?.start, 0); |
| assert.strictEqual(scopes?.end, 17); |
| assert.deepEqual(scopes?.variables?.size, 1); |
| assert.deepEqual(scopes?.variables?.get('x')?.uses.map(u => u.offset), [4]); |
| const blockScope = scopes?.children[0]; |
| assert.strictEqual(blockScope?.start, 7); |
| assert.strictEqual(blockScope?.end, 17); |
| assert.deepEqual(blockScope?.variables?.size, 1); |
| assert.deepEqual(blockScope?.variables?.get('y')?.uses.map(u => u.offset), [13]); |
| }); |
| |
| it('parses object destructuring', () => { |
| const source = 'let {x: y} = {}'; |
| const scopes = parseScopes(source); |
| |
| assert.exists(scopes); |
| assert.isEmpty(scopes.children); |
| assert.strictEqual(scopes.variables.size, 1); |
| const [[name, {uses}]] = scopes.variables; |
| assert.strictEqual(name, 'y'); |
| assert.lengthOf(uses, 1); |
| assert.strictEqual(uses[0].offset, source.indexOf('y')); |
| }); |
| |
| it('parses object destructuring with default values', () => { |
| const source = 'let {x: y = 42} = {}'; |
| const scopes = parseScopes(source); |
| |
| assert.exists(scopes); |
| assert.isEmpty(scopes.children); |
| assert.strictEqual(scopes.variables.size, 1); |
| const [[name, {uses}]] = scopes.variables; |
| assert.strictEqual(name, 'y'); |
| assert.lengthOf(uses, 1); |
| assert.strictEqual(uses[0].offset, source.indexOf('y')); |
| }); |
| |
| it('parses object destructuring with short-hand syntax', () => { |
| const source = 'let {x} = {}'; |
| const scopes = parseScopes(source); |
| |
| assert.exists(scopes); |
| assert.isEmpty(scopes.children); |
| assert.strictEqual(scopes.variables.size, 1); |
| const [[name, {uses}]] = scopes.variables; |
| assert.strictEqual(name, 'x'); |
| assert.lengthOf(uses, 1); |
| assert.strictEqual(uses[0].offset, source.indexOf('x')); |
| }); |
| |
| it('parses object destructuring with short-hand syntax and default values', () => { |
| const source = 'let {x = 42} = {}'; |
| const scopes = parseScopes(source); |
| |
| assert.exists(scopes); |
| assert.isEmpty(scopes.children); |
| assert.strictEqual(scopes.variables.size, 1); |
| const [[name, {uses}]] = scopes.variables; |
| assert.strictEqual(name, 'x'); |
| assert.lengthOf(uses, 1); |
| assert.strictEqual(uses[0].offset, source.indexOf('x')); |
| }); |
| |
| it('parses ES modules', () => { |
| const source = 'import * as Foo from "./foo.js"; Foo.foo();'; |
| const scopes = parseScopes(source, 'module'); |
| |
| assert.exists(scopes); |
| assert.isEmpty(scopes.children); |
| assert.strictEqual(scopes.variables.size, 1); |
| const [[name, {uses}]] = scopes.variables; |
| assert.strictEqual(name, 'Foo'); |
| assert.lengthOf(uses, 1); |
| const firstOccurence = source.indexOf('Foo'); |
| assert.strictEqual(uses[0].offset, source.indexOf('Foo', firstOccurence + 1)); |
| }); |
| |
| it('parses methods', () => { |
| const scopes = parseScopes(`class C { someMethod() {} }`); |
| |
| const scopeMethod = scopes?.children[0]; |
| assert.strictEqual(scopeMethod?.kind, FormatterAction.ScopeKind.FUNCTION); |
| assert.strictEqual(scopeMethod?.name, 'someMethod'); |
| assert.deepEqual(scopeMethod?.nameMappingLocations, [10, 20]); |
| }); |
| |
| it('parses private methods', () => { |
| const scopes = parseScopes(`class C { #someMethod() {} }`); |
| |
| const scopeMethod = scopes?.children[0]; |
| assert.strictEqual(scopeMethod?.kind, FormatterAction.ScopeKind.FUNCTION); |
| assert.strictEqual(scopeMethod?.name, '#someMethod'); |
| assert.deepEqual(scopeMethod?.nameMappingLocations, [10, 21]); |
| }); |
| |
| it('parses getters and setters', () => { |
| const scopes = parseScopes(`class C { get foo() {} set foo(value) {} }`); |
| |
| const scopeGet = scopes?.children[0]; |
| assert.strictEqual(scopeGet?.kind, FormatterAction.ScopeKind.FUNCTION); |
| assert.strictEqual(scopeGet?.name, 'foo'); |
| assert.deepEqual(scopeGet?.nameMappingLocations, [14, 17]); |
| |
| const scopeSet = scopes?.children[1]; |
| assert.strictEqual(scopeSet?.kind, FormatterAction.ScopeKind.FUNCTION); |
| assert.strictEqual(scopeGet?.name, 'foo'); |
| assert.deepEqual(scopeSet?.nameMappingLocations, [27, 30]); |
| }); |
| |
| it('parses method syntax in object literals', () => { |
| const scopes = parseScopes('const obj = { someMethod() {} };'); |
| |
| const scopeMethod = scopes?.children[0]; |
| assert.strictEqual(scopeMethod?.kind, FormatterAction.ScopeKind.FUNCTION); |
| assert.strictEqual(scopeMethod?.name, 'someMethod'); |
| assert.deepEqual(scopeMethod?.nameMappingLocations, [14, 24]); |
| }); |
| |
| it('parses async arrow functions', () => { |
| const scopes = parseScopes('const x = async y => await y;'); |
| |
| const scopeFn = scopes?.children[0]; |
| assert.strictEqual(scopeFn?.kind, FormatterAction.ScopeKind.ARROW_FUNCTION); |
| assert.deepEqual(scopeFn?.nameMappingLocations, [18]); |
| }); |
| |
| it('doesn\'t get confused by default values in arrow functions', () => { |
| const scopes = parseScopes('const x = (a = 42) => console.log(a);'); |
| |
| const scopeFn = scopes?.children[0]; |
| assert.strictEqual(scopeFn?.kind, FormatterAction.ScopeKind.ARROW_FUNCTION); |
| assert.deepEqual(scopeFn?.nameMappingLocations, [10, 19]); |
| }); |
| }); |
| }); |