tests: Port jsParse test to jasmine
Based on https://bugzilla.gnome.org/show_bug.cgi?id=783738 from Sam Spilsbury. Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3164>
This commit is contained in:
parent
40750f5849
commit
ab4ca87ee9
@ -23,6 +23,7 @@ unit_tests = [
|
|||||||
'highlighter',
|
'highlighter',
|
||||||
'injectionManager',
|
'injectionManager',
|
||||||
'insertSorted',
|
'insertSorted',
|
||||||
|
'jsParse',
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach test : unit_tests
|
foreach test : unit_tests
|
||||||
@ -41,7 +42,6 @@ foreach test : unit_tests
|
|||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
legacy_tests = [
|
legacy_tests = [
|
||||||
'jsParse',
|
|
||||||
'markup',
|
'markup',
|
||||||
'params',
|
'params',
|
||||||
'signalTracker',
|
'signalTracker',
|
||||||
|
@ -1,146 +1,249 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
// Test cases for MessageTray URLification
|
|
||||||
|
|
||||||
const JsUnit = imports.jsUnit;
|
|
||||||
|
|
||||||
import 'resource:///org/gnome/shell/ui/environment.js';
|
import 'resource:///org/gnome/shell/ui/environment.js';
|
||||||
|
|
||||||
import * as Assertions from '../common/assertions.js';
|
|
||||||
|
|
||||||
import * as JsParse from 'resource:///org/gnome/shell/misc/jsParse.js';
|
import * as JsParse from 'resource:///org/gnome/shell/misc/jsParse.js';
|
||||||
|
|
||||||
const HARNESS_COMMAND_HEADER = 'let imports = obj;' +
|
//
|
||||||
|
// Test javascript parsing
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// TODO: We probably want to change all these to use
|
||||||
|
// a table driven method, using for() inside
|
||||||
|
// a test body hampers readibility and
|
||||||
|
// debuggability when something goes wrong.
|
||||||
|
//
|
||||||
|
// NOTE: The inconsistent use of "" and '' quotes in this
|
||||||
|
// file is largely to handle nesting without passing
|
||||||
|
// escape markers to the functions under test. The
|
||||||
|
// preferred style in the shell is single quotes
|
||||||
|
// so we use that wherever possible.
|
||||||
|
|
||||||
|
describe('Matching quote search', () => {
|
||||||
|
const FindMatchingQuoteParameters = [
|
||||||
|
{
|
||||||
|
name: 'only double quotes',
|
||||||
|
input: "'double quotes'",
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'only single quotes',
|
||||||
|
input: '\'single quotes\'',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'some parts unquoted and other parts quoted',
|
||||||
|
input: "some unquoted 'some quoted'",
|
||||||
|
output: 14,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mixed quotes',
|
||||||
|
input: "'mixed \" quotes\"'",
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'escaped quotes',
|
||||||
|
input: "'escaped \\' quote'",
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const {name, input, output} of FindMatchingQuoteParameters) {
|
||||||
|
it(`finds a matching quote where there are ${name}`, () => {
|
||||||
|
const match = JsParse.findMatchingQuote(input, input.length - 1);
|
||||||
|
expect(match).toEqual(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Matching slash search', () => {
|
||||||
|
const FindMatchingSlashParameters = [
|
||||||
|
{
|
||||||
|
name: 'matching slashes',
|
||||||
|
input: '/slash/',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'matching slashes with extraneous characters in-between',
|
||||||
|
input: '/slash " with $ funny ^\' stuff/',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mathcing slashes with some parts unslashed',
|
||||||
|
input: 'some unslashed /some slashed/',
|
||||||
|
output: 15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'matching slashes with an escaped slash in the middle',
|
||||||
|
input: '/escaped \\/ slash/',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let {name, input, output} of FindMatchingSlashParameters) {
|
||||||
|
it(`finds a matching slash where there are ${name}`, () => {
|
||||||
|
let match = JsParse.findMatchingQuote(input, input.length - 1);
|
||||||
|
expect(match).toEqual(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Matching brace search', () => {
|
||||||
|
const FindMatchingBraceParameters = [
|
||||||
|
{
|
||||||
|
name: 'square braces',
|
||||||
|
input: '[square brace]',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'round braces',
|
||||||
|
input: '(round brace)',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'braces with nesting',
|
||||||
|
input: '([()][nesting!])',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'braces with quoted braces in the middle',
|
||||||
|
input: "[we have 'quoted [' braces]",
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'braces with regexed braces in the middle',
|
||||||
|
input: '[we have /regex [/ braces]',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mismatched braces',
|
||||||
|
input: '([[])[] mismatched braces ]',
|
||||||
|
output: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let {name, input, output} of FindMatchingBraceParameters) {
|
||||||
|
it(`finds matching braces where there are ${name}`, () => {
|
||||||
|
let match = JsParse.findMatchingBrace(input, input.length - 1);
|
||||||
|
expect(match).toEqual(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Beginning of expression search', () => {
|
||||||
|
const ExpressionOffsetParameters = [
|
||||||
|
{
|
||||||
|
name: 'object property name',
|
||||||
|
input: 'abc.123',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'function call result property name',
|
||||||
|
input: 'foo().bar',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'centre of malformed function call expression',
|
||||||
|
input: 'foo(bar',
|
||||||
|
output: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'complete nested expression',
|
||||||
|
input: 'foo[abc.match(/"/)]',
|
||||||
|
output: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const {name, input, output} of ExpressionOffsetParameters) {
|
||||||
|
it(`finds the beginning of a ${name}`, () => {
|
||||||
|
const match = JsParse.getExpressionOffset(input, input.length - 1);
|
||||||
|
expect(match).toEqual(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Constant variable search', () => {
|
||||||
|
const DeclaredConstantsParameters = [
|
||||||
|
{
|
||||||
|
name: 'two constants on one line with space between equals',
|
||||||
|
input: 'const foo = X; const bar = Y',
|
||||||
|
output: ['foo', 'bar'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'two constants on one line with no space between equlas',
|
||||||
|
input: 'const foo=X; const bar=Y;',
|
||||||
|
output: ['foo', 'bar'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const {name, input, output} of DeclaredConstantsParameters) {
|
||||||
|
it(`finds ${name}`, () => {
|
||||||
|
const match = JsParse.getDeclaredConstants(input);
|
||||||
|
expect(match).toEqual(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Expression safety determination', () => {
|
||||||
|
const UnsafeExpressionParams = [
|
||||||
|
{
|
||||||
|
name: 'property access',
|
||||||
|
input: 'foo.bar',
|
||||||
|
output: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'property access by array',
|
||||||
|
input: 'foo[\'bar\']',
|
||||||
|
output: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'expression with syntax error',
|
||||||
|
input: 'foo["a=b=c".match(/=/)',
|
||||||
|
output: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'property access by array with nested const expression',
|
||||||
|
input: 'foo[1==2]',
|
||||||
|
output: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bracketed assignment no whitespace',
|
||||||
|
input: '(x=4)',
|
||||||
|
output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bracked assignment with whitespace',
|
||||||
|
input: '(x = 4)',
|
||||||
|
output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bracketed implicit call',
|
||||||
|
input: '(x;y)',
|
||||||
|
output: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const {name, input, output} of UnsafeExpressionParams) {
|
||||||
|
const isOrIsNot = output ? 'is' : 'is not';
|
||||||
|
it(`finds that an expression which is a ${name} ${isOrIsNot} safe`, () => {
|
||||||
|
let unsafe = JsParse.isUnsafeExpression(input);
|
||||||
|
expect(unsafe).toEqual(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test safety of eval to get completions
|
||||||
|
//
|
||||||
|
describe('Expression evaluation', () => {
|
||||||
|
const HARNESS_COMMAND_HEADER =
|
||||||
|
'let imports = obj;' +
|
||||||
'let global = obj;' +
|
'let global = obj;' +
|
||||||
'let Main = obj;' +
|
'let Main = obj;' +
|
||||||
'let foo = obj;' +
|
'let foo = obj;' +
|
||||||
'let r = obj;';
|
'let r = obj;';
|
||||||
|
|
||||||
const testsFindMatchingQuote = [
|
const ExpressionParameters = [
|
||||||
{
|
|
||||||
input: '"double quotes"',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '\'single quotes\'',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'some unquoted "some quoted"',
|
|
||||||
output: 14,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '"mixed \' quotes\'"',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '"escaped \\" quote"',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const testsFindMatchingSlash = [
|
|
||||||
{
|
|
||||||
input: '/slash/',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '/slash " with $ funny ^\' stuff/',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'some unslashed /some slashed/',
|
|
||||||
output: 15,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '/escaped \\/ slash/',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const testsFindMatchingBrace = [
|
|
||||||
{
|
|
||||||
input: '[square brace]',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '(round brace)',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '([()][nesting!])',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '[we have "quoted [" braces]',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '[we have /regex [/ braces]',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '([[])[] mismatched braces ]',
|
|
||||||
output: 1,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const testsGetExpressionOffset = [
|
|
||||||
{
|
|
||||||
input: 'abc.123',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'foo().bar',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'foo(bar',
|
|
||||||
output: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'foo[abc.match(/"/)]',
|
|
||||||
output: 0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const testsGetDeclaredConstants = [
|
|
||||||
{
|
|
||||||
input: 'const foo = X; const bar = Y;',
|
|
||||||
output: ['foo', 'bar'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'const foo=X; const bar=Y',
|
|
||||||
output: ['foo', 'bar'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const testsIsUnsafeExpression = [
|
|
||||||
{
|
|
||||||
input: 'foo.bar',
|
|
||||||
output: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'foo[\'bar\']',
|
|
||||||
output: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'foo["a=b=c".match(/=/)',
|
|
||||||
output: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'foo[1==2]',
|
|
||||||
output: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '(x=4)',
|
|
||||||
output: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '(x = 4)',
|
|
||||||
output: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '(x;y)',
|
|
||||||
output: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const testsModifyScope = [
|
|
||||||
"foo['a",
|
"foo['a",
|
||||||
"foo()['b'",
|
"foo()['b'",
|
||||||
"obj.foo()('a', 1, 2, 'b')().",
|
"obj.foo()('a', 1, 2, 'b')().",
|
||||||
@ -150,90 +253,36 @@ const testsModifyScope = [
|
|||||||
'Main.foo.bar = 3; bar.',
|
'Main.foo.bar = 3; bar.',
|
||||||
'(Main.foo = 3).',
|
'(Main.foo = 3).',
|
||||||
'Main[Main.foo+=-1].',
|
'Main[Main.foo+=-1].',
|
||||||
];
|
];
|
||||||
|
|
||||||
//
|
|
||||||
// Test javascript parsing
|
|
||||||
//
|
|
||||||
|
|
||||||
for (let i = 0; i < testsFindMatchingQuote.length; i++) {
|
|
||||||
let text = testsFindMatchingQuote[i].input;
|
|
||||||
let match = JsParse.findMatchingQuote(text, text.length - 1);
|
|
||||||
|
|
||||||
JsUnit.assertEquals(`Test testsFindMatchingQuote ${i}`,
|
|
||||||
match, testsFindMatchingQuote[i].output);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < testsFindMatchingSlash.length; i++) {
|
|
||||||
let text = testsFindMatchingSlash[i].input;
|
|
||||||
let match = JsParse.findMatchingSlash(text, text.length - 1);
|
|
||||||
|
|
||||||
JsUnit.assertEquals(`Test testsFindMatchingSlash ${i}`,
|
|
||||||
match, testsFindMatchingSlash[i].output);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < testsFindMatchingBrace.length; i++) {
|
|
||||||
let text = testsFindMatchingBrace[i].input;
|
|
||||||
let match = JsParse.findMatchingBrace(text, text.length - 1);
|
|
||||||
|
|
||||||
JsUnit.assertEquals(`Test testsFindMatchingBrace ${i}`,
|
|
||||||
match, testsFindMatchingBrace[i].output);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < testsGetExpressionOffset.length; i++) {
|
|
||||||
let text = testsGetExpressionOffset[i].input;
|
|
||||||
let match = JsParse.getExpressionOffset(text, text.length - 1);
|
|
||||||
|
|
||||||
JsUnit.assertEquals(`Test testsGetExpressionOffset ${i}`,
|
|
||||||
match, testsGetExpressionOffset[i].output);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < testsGetDeclaredConstants.length; i++) {
|
|
||||||
let text = testsGetDeclaredConstants[i].input;
|
|
||||||
let match = JsParse.getDeclaredConstants(text);
|
|
||||||
|
|
||||||
Assertions.assertArrayEquals(`Test testsGetDeclaredConstants ${i}`,
|
|
||||||
match, testsGetDeclaredConstants[i].output);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < testsIsUnsafeExpression.length; i++) {
|
|
||||||
let text = testsIsUnsafeExpression[i].input;
|
|
||||||
let unsafe = JsParse.isUnsafeExpression(text);
|
|
||||||
|
|
||||||
JsUnit.assertEquals(`Test testsIsUnsafeExpression ${i}`,
|
|
||||||
unsafe, testsIsUnsafeExpression[i].output);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Test safety of eval to get completions
|
|
||||||
//
|
|
||||||
|
|
||||||
for (let i = 0; i < testsModifyScope.length; i++) {
|
|
||||||
let text = testsModifyScope[i];
|
|
||||||
|
|
||||||
const globalPropsPre = Object.getOwnPropertyNames(globalThis).sort();
|
|
||||||
|
|
||||||
|
function evalIfSafe(text) {
|
||||||
// Just as in JsParse.getCompletions, we will find the offset
|
// Just as in JsParse.getCompletions, we will find the offset
|
||||||
// of the expression, test whether it is unsafe, and then eval it.
|
// of the expression, test whether it is unsafe, and then eval it.
|
||||||
let offset = JsParse.getExpressionOffset(text, text.length - 1);
|
const offset = JsParse.getExpressionOffset(text, text.length - 1);
|
||||||
if (offset >= 0) {
|
if (offset < 0)
|
||||||
text = text.slice(offset);
|
return;
|
||||||
|
|
||||||
let matches = text.match(/(.*)\.(.*)/);
|
const matches = text.slice(offset).match(/(.*)\.(.*)/);
|
||||||
if (matches) {
|
if (matches == null)
|
||||||
let [, base] = matches;
|
return;
|
||||||
|
|
||||||
|
const [, base] = matches;
|
||||||
|
if (JsParse.isUnsafeExpression(base))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!JsParse.isUnsafeExpression(base)) {
|
|
||||||
try {
|
|
||||||
eval(HARNESS_COMMAND_HEADER + base);
|
eval(HARNESS_COMMAND_HEADER + base);
|
||||||
} catch (e) {
|
|
||||||
JsUnit.assertNotEquals(`Code '${base}' is valid code`, e.constructor, SyntaxError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const expression of ExpressionParameters) {
|
||||||
|
const globalPropsPre = Object.getOwnPropertyNames(globalThis).sort();
|
||||||
|
|
||||||
|
it(`of ${expression} does not throw syntax errors with a known safe expression`, () => {
|
||||||
|
expect(() => evalIfSafe(expression)).not.toThrow(SyntaxError);
|
||||||
|
});
|
||||||
|
|
||||||
const globalPropsPost = Object.getOwnPropertyNames(globalThis).sort();
|
const globalPropsPost = Object.getOwnPropertyNames(globalThis).sort();
|
||||||
Assertions.assertArrayEquals('The global object was not modified',
|
it(`of ${expression} does not modify the global scope`, () => {
|
||||||
globalPropsPre, globalPropsPost);
|
expect(globalPropsPre).toEqual(globalPropsPost);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user