Skip to content
File

Blob: types/test/generator/structure.spec.ts

typescript292 lines
1// Copyright (c) 2022-2023 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5import assert from 'assert';
6import { test } from 'node:test';
7import { JsgImplType_Type, Structure } from '@workerd/jsg/rtti';
8import { Message } from 'capnp-es';
9import { createStructureNode } from '../../src/generator/structure';
10import { printNode } from '../../src/print';
11 
12test('createStructureNode: method members', () => {
13 const structure = new Message().initRoot(Structure);
14 structure.name = 'Methods';
15 structure.fullyQualifiedName = 'workerd::api::Methods';
16 
17 const members = structure._initMembers(3);
18 
19 let method = members.get(0)._initMethod();
20 method.name = 'one';
21 {
22 const args = method._initArgs(3);
23 args.get(0).boolt = true;
24 args.get(1)._initPromise()._initValue().voidt = true;
25 const maybe = args.get(2)._initMaybe();
26 maybe.name = 'jsg::Optional';
27 maybe._initValue()._initNumber().name = 'int';
28 }
29 method._initReturnType().voidt = true;
30 
31 method = members.get(1)._initMethod();
32 method.name = 'two';
33 method._initReturnType()._initJsgImpl().type =
34 JsgImplType_Type.JSG_UNIMPLEMENTED;
35 
36 method = members.get(2)._initMethod();
37 method.name = 'three';
38 {
39 const args = method._initArgs(1);
40 args.get(0)._initJsgImpl().type = JsgImplType_Type.JSG_VARARGS;
41 }
42 method._initReturnType().boolt = true;
43 
44 // Note method with unimplemented return is omitted
45 assert.strictEqual(
46 printNode(createStructureNode(structure, { asClass: false })),
47 `interface Methods {
48 one(param0: boolean, param1: Promise<any>, param2?: number): void;
49 three(...param0: any[]): boolean;
50}`
51 );
52 
53 method.static = true;
54 assert.strictEqual(
55 printNode(createStructureNode(structure, { asClass: true })),
56 `declare abstract class Methods {
57 one(param0: boolean, param1: Promise<any>, param2?: number): void;
58 static three(...param0: any[]): boolean;
59}`
60 );
61});
62 
63test('createStructureNode: property members', () => {
64 const structure = new Message().initRoot(Structure);
65 structure.name = 'Properties';
66 structure.fullyQualifiedName = 'workerd::api::Properties';
67 
68 const members = structure._initMembers(8);
69 
70 let prop = members.get(0)._initProperty();
71 prop.name = 'one';
72 prop._initType().boolt = true;
73 
74 prop = members.get(1)._initProperty();
75 prop.name = 'two';
76 prop._initType()._initNumber().name = 'int';
77 prop.readonly = true;
78 
79 prop = members.get(2)._initProperty();
80 prop.name = 'three';
81 {
82 const maybe = prop._initType()._initMaybe();
83 maybe.name = 'jsg::Optional';
84 maybe._initValue().boolt = true;
85 }
86 prop.readonly = true;
87 
88 prop = members.get(3)._initProperty();
89 prop.name = 'four';
90 prop._initType()._initJsgImpl().type = JsgImplType_Type.JSG_UNIMPLEMENTED;
91 prop.readonly = true;
92 
93 prop = members.get(4)._initProperty();
94 prop.name = 'five';
95 prop._initType().boolt = true;
96 prop.prototype = true;
97 
98 prop = members.get(5)._initProperty();
99 prop.name = 'six';
100 prop._initType()._initString().name = 'kj::String';
101 prop.readonly = true;
102 prop.prototype = true;
103 
104 prop = members.get(6)._initProperty();
105 prop.name = 'seven';
106 {
107 const maybe = prop._initType()._initMaybe();
108 maybe.name = 'jsg::Optional';
109 maybe._initValue()._initNumber().name = 'short';
110 }
111 prop.readonly = true;
112 prop.prototype = true;
113 
114 prop = members.get(7)._initProperty();
115 prop.name = 'eight';
116 prop._initType()._initJsgImpl().type = JsgImplType_Type.JSG_UNIMPLEMENTED;
117 prop.readonly = true;
118 prop.prototype = true;
119 
120 // Note unimplemented properties omitted
121 assert.strictEqual(
122 printNode(createStructureNode(structure, { asClass: false })),
123 `interface Properties {
124 one: boolean;
125 readonly two: number;
126 readonly three?: boolean;
127 get five(): boolean;
128 set five(value: boolean);
129 get six(): string;
130 get seven(): number | undefined;
131}`
132 );
133 assert.strictEqual(
134 printNode(createStructureNode(structure, { asClass: true })),
135 `declare abstract class Properties {
136 one: boolean;
137 readonly two: number;
138 readonly three?: boolean;
139 get five(): boolean;
140 set five(value: boolean);
141 get six(): string;
142 get seven(): number | undefined;
143}`
144 );
145});
146 
147test('createStructureNode: nested type members', () => {
148 const structure = new Message().initRoot(Structure);
149 structure.name = 'Nested';
150 structure.fullyQualifiedName = 'workerd::api::Nested';
151 
152 const members = structure._initMembers(2);
153 
154 let nested = members.get(0)._initNested();
155 let nestedStructure = nested._initStructure();
156 nestedStructure.name = 'Thing';
157 nestedStructure.fullyQualifiedName = 'workerd::api::Thing';
158 
159 nested = members.get(1)._initNested();
160 nestedStructure = nested._initStructure();
161 nestedStructure.name = 'OtherThing';
162 nestedStructure.fullyQualifiedName = 'workerd::api::OtherThing';
163 nested.name = 'RenamedThing';
164 
165 assert.strictEqual(
166 printNode(createStructureNode(structure, { asClass: false })),
167 `interface Nested {
168 Thing: typeof Thing;
169 RenamedThing: typeof OtherThing;
170}`
171 );
172 assert.strictEqual(
173 printNode(createStructureNode(structure, { asClass: true })),
174 `declare abstract class Nested {
175 Thing: typeof Thing;
176 RenamedThing: typeof OtherThing;
177}`
178 );
179});
180 
181test('createStructureNode: constant members', () => {
182 const structure = new Message().initRoot(Structure);
183 structure.name = 'Constants';
184 structure.fullyQualifiedName = 'workerd::api::Constants';
185 
186 const members = structure._initMembers(1);
187 
188 const constant = members.get(0)._initConstant();
189 constant.name = 'THING';
190 constant.value = BigInt(42);
191 
192 assert.strictEqual(
193 printNode(createStructureNode(structure, { asClass: true })),
194 `declare abstract class Constants {
195 static readonly THING: number;
196}`
197 );
198});
199 
200test('createStructureNode: iterator members', () => {
201 const structure = new Message().initRoot(Structure);
202 structure.name = 'Iterators';
203 structure.fullyQualifiedName = 'workerd::api::Iterators';
204 
205 structure.iterable = true;
206 const iterator = structure._initIterator();
207 {
208 const args = iterator._initArgs(1);
209 const maybe = args.get(0)._initMaybe();
210 maybe.name = 'jsg::Optional';
211 const optionsStructure = maybe._initValue()._initStructure();
212 optionsStructure.name = 'ThingOptions';
213 optionsStructure.fullyQualifiedName = 'workerd::api::ThingOptions';
214 }
215 let returnStructure = iterator._initReturnType()._initStructure();
216 returnStructure.name = 'ThingIterator';
217 returnStructure.fullyQualifiedName = 'workerd::api::ThingIterator';
218 
219 structure.asyncIterable = true;
220 const asyncIterator = structure._initAsyncIterator();
221 {
222 const args = asyncIterator._initArgs(1);
223 const maybe = args.get(0)._initMaybe();
224 maybe.name = 'jsg::Optional';
225 const optionsStructure = maybe._initValue()._initStructure();
226 optionsStructure.name = 'AsyncThingOptions';
227 optionsStructure.fullyQualifiedName = 'workerd::api::AsyncThingOptions';
228 }
229 returnStructure = asyncIterator._initReturnType()._initStructure();
230 returnStructure.name = 'AsyncThingIterator';
231 returnStructure.fullyQualifiedName = 'workerd::api::AsyncThingIterator';
232 
233 assert.strictEqual(
234 printNode(createStructureNode(structure, { asClass: false })),
235 `interface Iterators {
236 [Symbol.iterator](param0?: ThingOptions): ThingIterator;
237 [Symbol.asyncIterator](param0?: AsyncThingOptions): AsyncThingIterator;
238}`
239 );
240 assert.strictEqual(
241 printNode(createStructureNode(structure, { asClass: true })),
242 `declare abstract class Iterators {
243 [Symbol.iterator](param0?: ThingOptions): ThingIterator;
244 [Symbol.asyncIterator](param0?: AsyncThingOptions): AsyncThingIterator;
245}`
246 );
247});
248 
249test('createStructureNode: constructors', () => {
250 const structure = new Message().initRoot(Structure);
251 structure.name = 'Constructor';
252 structure.fullyQualifiedName = 'workerd::api::Constructor';
253 
254 const members = structure._initMembers(1);
255 
256 const constructor = members.get(0)._initConstructor();
257 {
258 const args = constructor._initArgs(4);
259 let maybe = args.get(0)._initMaybe();
260 maybe.name = 'jsg::Optional';
261 maybe._initValue().boolt = true;
262 args.get(1)._initString().name = 'kj::String';
263 args.get(2)._initPromise()._initValue().voidt = true;
264 maybe = args.get(3)._initMaybe();
265 maybe.name = 'jsg::Optional';
266 maybe._initValue()._initNumber().name = 'int';
267 }
268 
269 assert.strictEqual(
270 printNode(createStructureNode(structure, { asClass: true })),
271 `declare class Constructor {
272 constructor(param0: boolean | undefined, param1: string, param2: Promise<any>, param3?: number);
273}`
274 );
275});
276 
277test('createStructureNode: extends', () => {
278 const structure = new Message().initRoot(Structure);
279 structure.name = 'Extends';
280 structure.fullyQualifiedName = 'workerd::api::Extends';
281 
282 const extendsStructure = structure._initExtends()._initStructure();
283 extendsStructure.name = 'Base';
284 extendsStructure.fullyQualifiedName = 'workerd::api::Base';
285 
286 assert.strictEqual(
287 printNode(createStructureNode(structure, { asClass: true })),
288 `declare abstract class Extends extends Base {
289}`
290 );
291});