Update javascript_natives/index.js script
This change also includes a manual run of the script to update
NativeFunctions.js.
Change-Id: I9ce1c4979674d9513cb7259e3e311e30b13de9ed
Bug: 1255619
Doc: https://goo.gle/devtools-js-argument-hints
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3399837
Reviewed-by: Mathias Bynens <mathias@chromium.org>
Commit-Queue: Johan Bay <jobay@chromium.org>
diff --git a/scripts/javascript_natives/tests.js b/scripts/javascript_natives/tests.js
new file mode 100644
index 0000000..84e3499
--- /dev/null
+++ b/scripts/javascript_natives/tests.js
@@ -0,0 +1,195 @@
+// Copyright 2022 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 * as assert from 'assert';
+import ts from 'typescript';
+import * as WebIDL2 from 'webidl2';
+
+import {clearState, parseTSFunction, postProcess, walkRoot} from './helpers.js';
+
+describe('NativeFunction signature generation', function() {
+ this.afterEach(() => {
+ clearState();
+ });
+
+ it('should produce correct signatures for IDL interface', function() {
+ WebIDL2
+ .parse(`
+[
+ Exposed=Window
+] interface Document : Node {
+ [CallWith=Document] constructor();
+ [Affects=Nothing] HTMLCollection getElementsByTagName(DOMString localName);
+ [Affects=Nothing] HTMLCollection getElementsByTagNameNS(DOMString? namespaceURI, DOMString localName);
+ [Affects=Nothing] HTMLCollection getElementsByClassName(DOMString classNames);
+
+ [NewObject, DoNotTestNewObject, PerWorldBindings, RaisesException, ImplementedAs=CreateElementForBinding] Element createElement(DOMString localName);
+ [NewObject, DoNotTestNewObject, RaisesException] Element createElementNS(DOMString? namespaceURI, DOMString qualifiedName);
+ [NewObject] DocumentFragment createDocumentFragment();
+ [NewObject] Text createTextNode(DOMString data);
+};
+`).forEach(walkRoot);
+ const output = postProcess(/* dryRun: */ true);
+ const expected = `export const NativeFunctions = [
+ {
+ name: "getElementsByTagName",
+ signatures: [["localName"]]
+ },
+ {
+ name: "getElementsByTagNameNS",
+ signatures: [["namespaceURI","localName"]]
+ },
+ {
+ name: "getElementsByClassName",
+ signatures: [["classNames"]]
+ },
+ {
+ name: "createElement",
+ signatures: [["localName"]]
+ },
+ {
+ name: "createElementNS",
+ signatures: [["namespaceURI","qualifiedName"]]
+ },
+ {
+ name: "createTextNode",
+ signatures: [["data"]]
+ }
+];`;
+ assert.equal(output, expected);
+ });
+
+ it('should produce correct signatures for IDL mixin interface', function() {
+ WebIDL2
+ .parse(`[
+ LegacyTreatAsPartialInterface,
+ Exposed=(Window,Worker)
+] interface mixin WindowOrWorkerGlobalScope {
+ [CallWith=ScriptState] void reportError(any e);
+
+ [RaisesException] DOMString atob(DOMString atob);
+ [CallWith=ScriptState, RuntimeCallStatsCounter=WindowSetTimeout] long setTimeout(Function handler, optional long timeout = 0, any... arguments);
+ [CallWith=ScriptState] long setTimeout(ScriptString handler, optional long timeout = 0, any... arguments);
+};
+`).forEach(walkRoot);
+ const output = postProcess(/* dryRun: */ true);
+ const expected = `export const NativeFunctions = [
+ {
+ name: "reportError",
+ signatures: [["e"]]
+ },
+ {
+ name: "atob",
+ signatures: [["atob"]]
+ },
+ {
+ name: "setTimeout",
+ signatures: [["handler","?timeout","...arguments"]]
+ }
+];`;
+ assert.equal(output, expected);
+ });
+
+ it('should produce correct signatures for Console IDL', function() {
+ WebIDL2
+ .parse(`
+[Exposed=(Window,Worker,Worklet)]
+namespace console {
+ undefined assert(optional boolean condition = false, any... data);
+ undefined table(optional any tabularData, optional sequence<DOMString> properties);
+ undefined count(optional DOMString label = "default");
+ undefined groupEnd();
+};
+`).forEach(walkRoot);
+ const output = postProcess(/* dryRun: */ true);
+ const expected = `export const NativeFunctions = [
+ {
+ name: "assert",
+ signatures: [["?condition","...data"]]
+ },
+ {
+ name: "table",
+ signatures: [["?tabularData","?properties"]]
+ },
+ {
+ name: "count",
+ signatures: [["?label"]]
+ }
+];`;
+ assert.equal(output, expected);
+ });
+
+ it('should produce correct signatures for Console IDL', function() {
+ WebIDL2
+ .parse(`
+// https://html.spec.whatwg.org/C/#the-slot-element
+[
+ Exposed=Window,
+ HTMLConstructor
+] interface HTMLSlotElement : HTMLElement {
+ [CEReactions, Reflect] attribute DOMString name;
+ [ImplementedAs=AssignedNodesForBinding] sequence<Node> assignedNodes(optional AssignedNodesOptions options = {});
+ [ImplementedAs=AssignedElementsForBinding] sequence<Element> assignedElements(optional AssignedNodesOptions options = {});
+ [RaisesException] void assign((Element or Text)... nodes);
+};
+`).forEach(walkRoot);
+ const output = postProcess(/* dryRun: */ true);
+ const expected = `export const NativeFunctions = [
+ {
+ name: "assignedNodes",
+ signatures: [["?options"]]
+ },
+ {
+ name: "assignedElements",
+ signatures: [["?options"]]
+ },
+ {
+ name: "assign",
+ signatures: [["...nodes"]]
+ }
+];`;
+ assert.equal(output, expected);
+ });
+
+ it('should produce correct signatures for typescript typings', function() {
+ const program = ts.createProgram(
+ [
+ new URL('test.d.ts', import.meta.url).pathname,
+ ],
+ {noLib: true, types: []});
+
+ for (const file of program.getSourceFiles()) {
+ ts.forEachChild(file, node => {
+ if (node.kind === ts.SyntaxKind.InterfaceDeclaration) {
+ for (const member of node.members) {
+ if (member.kind === ts.SyntaxKind.MethodSignature) {
+ parseTSFunction(member, node);
+ }
+ }
+ }
+ if (node.kind === ts.SyntaxKind.FunctionDeclaration) {
+ parseTSFunction(node, {name: {text: 'Window'}});
+ }
+ });
+ }
+ const output = postProcess(/* dryRun: */ true);
+ const expected = `export const NativeFunctions = [
+ {
+ name: "at",
+ signatures: [["index"]]
+ },
+ {
+ name: "diffSig",
+ signatures: [["oneSig"]],
+ receiver: "Array"
+ },
+ {
+ name: "diffSig",
+ signatures: [["twoSig"]],
+ receiver: "ReadonlyArray"
+ }
+];`;
+ assert.equal(output, expected);
+ });
+});