Skip to content

Commit b729a75

Browse files
committed
test(browser): add test for IR and WASM platforms
1 parent f1bc675 commit b729a75

File tree

8 files changed

+181
-17
lines changed

8 files changed

+181
-17
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"@babel/plugin-transform-runtime": "~7.22.4",
2121
"@babel/preset-env": "~7.22.4",
2222
"@babel/runtime-corejs2": "~7.22.2",
23-
"@playwright/test": "^1.37.1",
23+
"@playwright/test": "^1.40.1",
2424
"@typescript-eslint/eslint-plugin": "^6.4.0",
2525
"@typescript-eslint/parser": "^6.4.0",
2626
"babel-loader": "^9.1.3",

tests/crosslink.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ test.describe('crosslink: library', () => {
1818
checkLink(generateCrosslink('simple'), { code: 'simple' });
1919

2020
// Pass platforms with codeWithSample
21-
checkLink(generateCrosslink('platform', { targetPlatform: 'JAVA' }), {
21+
checkLink(generateCrosslink('platform', { targetPlatform: 'js-ir' }), {
2222
code: 'platform',
23-
targetPlatform: 'JAVA',
23+
targetPlatform: 'js-ir',
2424
});
2525

2626
// Invalid target

tests/restrictions.e2e.ts

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import { expect, Page, test } from '@playwright/test';
2+
3+
import { readFileSync } from 'fs';
4+
import { join } from 'path';
5+
6+
import { gotoHtmlWidget } from './utlis/server/playground';
7+
8+
import { RESULT_SELECTOR, WIDGET_SELECTOR } from './utlis/selectors';
9+
10+
import { prepareNetwork, printlnCode } from './utlis';
11+
import { mockRunRequest, waitRunRequest } from './utlis/mocks/compiler';
12+
import { runButton } from './utlis/interactions';
13+
import { makeJSPrintCode } from './utlis/mocks/result';
14+
15+
const OUTPUTS = Object.freeze({
16+
'js-ir': {
17+
jsCode: makeJSPrintCode('Hello, world!'),
18+
errors: { 'File.kt': [] },
19+
exception: null,
20+
text: '<outStream>Hello, world!\n</outStream>',
21+
},
22+
wasm: JSON.parse(
23+
readFileSync(join(__dirname, 'utlis/mocks/wasm.json'), 'utf-8'),
24+
),
25+
});
26+
27+
const VERSIONS = [
28+
{ version: '1.3.10' },
29+
{ version: '1.9.20', latestStable: true },
30+
{ version: '2.0.1' },
31+
] as const;
32+
33+
test.describe('platform restrictions', () => {
34+
test.beforeEach(async ({ page, baseURL }) => {
35+
await prepareNetwork(page, baseURL, {
36+
versions: (route) =>
37+
route.fulfill({
38+
body: JSON.stringify(VERSIONS),
39+
}),
40+
}); // offline mode
41+
});
42+
43+
test('JS_IR for unsupported version', async ({ page }) => {
44+
await shouldFailedRun(
45+
page,
46+
'js-ir',
47+
'1.3.10',
48+
'JS IR compiler backend accessible only since 1.5.0 version',
49+
);
50+
});
51+
52+
test('JS_IR for supported by minor version', async ({ page }) => {
53+
await shouldSuccessRun(page, 'js-ir', '1.9.0');
54+
});
55+
56+
test('JS_IR for supported by major version', async ({ page }) => {
57+
await shouldSuccessRun(page, 'js-ir', '2.0.1');
58+
});
59+
60+
test('WASM for unsupported version', async ({ page }) => {
61+
await shouldFailedRun(
62+
page,
63+
'wasm',
64+
'1.3.10',
65+
'Wasm compiler backend accessible only since 1.9.0 version',
66+
);
67+
});
68+
69+
test('WASM for supported by minor version', async ({ page, browserName }) => {
70+
test.skip(
71+
browserName !== 'chromium',
72+
"WASM doesn't supported in this browser",
73+
);
74+
await shouldSuccessRun(page, 'wasm', '1.9.0');
75+
});
76+
77+
test('WASM for supported by major version', async ({ page, browserName }) => {
78+
test.skip(
79+
browserName !== 'chromium',
80+
"WASM doesn't supported in this browser",
81+
);
82+
await shouldSuccessRun(page, 'wasm', '2.0.1');
83+
});
84+
});
85+
86+
async function shouldSuccessRun(
87+
page: Page,
88+
platform: keyof typeof OUTPUTS,
89+
version: string,
90+
) {
91+
await gotoHtmlWidget(
92+
page,
93+
{ selector: 'code', version: version },
94+
/* language=html */ `
95+
<code data-target-platform='${platform}'>${printlnCode(
96+
'Hello, world!',
97+
)}</code>
98+
`,
99+
);
100+
101+
const resolveRun = await mockRunRequest(page);
102+
103+
const editor = page.locator(WIDGET_SELECTOR);
104+
105+
await Promise.all([waitRunRequest(page), runButton(editor)]);
106+
107+
resolveRun({
108+
json: Object.freeze(OUTPUTS[platform]),
109+
});
110+
111+
// playground loaded
112+
await expect(editor.locator(RESULT_SELECTOR)).toBeVisible();
113+
await expect(editor.locator(RESULT_SELECTOR)).toContainText('Hello, world!');
114+
}
115+
116+
async function shouldFailedRun(
117+
page: Page,
118+
platform: string,
119+
version: string,
120+
text: string,
121+
) {
122+
await gotoHtmlWidget(
123+
page,
124+
{ selector: 'code', version: version },
125+
/* language=html */ `
126+
<code data-target-platform='${platform}'>${printlnCode(
127+
'Hello, world!',
128+
)}</code>
129+
`,
130+
);
131+
132+
const editor = page.locator(WIDGET_SELECTOR);
133+
await runButton(editor);
134+
135+
await expect(editor.locator(RESULT_SELECTOR)).toBeVisible();
136+
await expect(
137+
editor.locator(RESULT_SELECTOR).locator('.test-fail'),
138+
).toContainText(text);
139+
}

tests/utlis/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ export async function refuseExternalUrls(
1919
export async function prepareNetwork(
2020
page: Page | BrowserContext,
2121
baseURL: string,
22+
options?: {
23+
versions: Parameters<typeof mockVersions>[1];
24+
},
2225
) {
2326
const unRefuse = await refuseExternalUrls(page, baseURL);
24-
const unVersions = await mockVersions(page);
27+
const unVersions = await mockVersions(page, options?.versions);
2528

2629
return async () => {
2730
await unVersions();

tests/utlis/mocks/compiler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ function isRunRequest(url: URL | string) {
3030

3131
return (
3232
uri.host === API_HOST &&
33-
uri.pathname.match(/^\/?\/api\/\d+\.\d+\.\d+\/compiler\/run$/) !== null
33+
(uri.pathname.match(/^\/?\/api\/\d+\.\d+\.\d+\/compiler\/run$/) !== null ||
34+
uri.pathname.match(/^\/?\/api\/\d+\.\d+\.\d+\/compiler\/translate$/) !==
35+
null)
3436
);
3537
}
3638

tests/utlis/mocks/result.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function makeJSPrintCode(text: string) {
2+
return `var moduleId = function (_) {\n 'use strict';\n //region block: pre-declaration\n setMetadataFor(Unit, 'Unit', objectMeta);\n setMetadataFor(BaseOutput, 'BaseOutput', classMeta);\n setMetadataFor(NodeJsOutput, 'NodeJsOutput', classMeta, BaseOutput);\n setMetadataFor(BufferedOutput, 'BufferedOutput', classMeta, BaseOutput, VOID, BufferedOutput);\n setMetadataFor(BufferedOutputToConsoleLog, 'BufferedOutputToConsoleLog', classMeta, BufferedOutput, VOID, BufferedOutputToConsoleLog);\n //endregion\n function Unit() {\n }\n protoOf(Unit).toString = function () {\n return 'kotlin.Unit';\n };\n var Unit_instance;\n function Unit_getInstance() {\n return Unit_instance;\n }\n function get_output() {\n _init_properties_console_kt__rfg7jv();\n return output;\n }\n var output;\n function BaseOutput() {\n }\n function NodeJsOutput(outputStream) {\n BaseOutput.call(this);\n this.outputStream_1 = outputStream;\n }\n protoOf(NodeJsOutput).print_o1pwgy_k$ = function (message) {\n // Inline function 'kotlin.io.String' call\n var messageString = String(message);\n this.outputStream_1.write(messageString);\n };\n function BufferedOutputToConsoleLog() {\n BufferedOutput.call(this);\n }\n protoOf(BufferedOutputToConsoleLog).print_o1pwgy_k$ = function (message) {\n // Inline function 'kotlin.io.String' call\n var s = String(message);\n // Inline function 'kotlin.text.nativeLastIndexOf' call\n // Inline function 'kotlin.js.asDynamic' call\n var i = s.lastIndexOf('\\n', 0);\n if (i >= 0) {\n var tmp = this;\n var tmp_0 = this.buffer_1;\n // Inline function 'kotlin.text.substring' call\n // Inline function 'kotlin.js.asDynamic' call\n tmp.buffer_1 = tmp_0 + s.substring(0, i);\n this.flush_shahbo_k$();\n // Inline function 'kotlin.text.substring' call\n var this_0 = s;\n var startIndex = i + 1 | 0;\n // Inline function 'kotlin.js.asDynamic' call\n s = this_0.substring(startIndex);\n }\n this.buffer_1 = this.buffer_1 + s;\n };\n protoOf(BufferedOutputToConsoleLog).flush_shahbo_k$ = function () {\n console.log(this.buffer_1);\n this.buffer_1 = '';\n };\n function BufferedOutput() {\n BaseOutput.call(this);\n this.buffer_1 = '';\n }\n protoOf(BufferedOutput).print_o1pwgy_k$ = function (message) {\n var tmp = this;\n var tmp_0 = this.buffer_1;\n // Inline function 'kotlin.io.String' call\n tmp.buffer_1 = tmp_0 + String(message);\n };\n function print(message) {\n _init_properties_console_kt__rfg7jv();\n get_output().print_o1pwgy_k$(message);\n }\n var properties_initialized_console_kt_gll9dl;\n function _init_properties_console_kt__rfg7jv() {\n if (!properties_initialized_console_kt_gll9dl) {\n properties_initialized_console_kt_gll9dl = true;\n // Inline function 'kotlin.run' call\n // Inline function 'kotlin.contracts.contract' call\n // Inline function 'kotlin.io.output.<anonymous>' call\n var isNode = typeof process !== 'undefined' && process.versions && !!process.versions.node;\n output = isNode ? new NodeJsOutput(process.stdout) : new BufferedOutputToConsoleLog();\n }\n }\n function implement(interfaces) {\n var maxSize = 1;\n var masks = [];\n var inductionVariable = 0;\n var last = interfaces.length;\n while (inductionVariable < last) {\n var i = interfaces[inductionVariable];\n inductionVariable = inductionVariable + 1 | 0;\n var currentSize = maxSize;\n var tmp1_elvis_lhs = i.prototype.$imask$;\n var imask = tmp1_elvis_lhs == null ? i.$imask$ : tmp1_elvis_lhs;\n if (!(imask == null)) {\n masks.push(imask);\n currentSize = imask.length;\n }\n var iid = i.$metadata$.iid;\n var tmp;\n if (iid == null) {\n tmp = null;\n } else {\n // Inline function 'kotlin.let' call\n // Inline function 'kotlin.contracts.contract' call\n // Inline function 'kotlin.js.implement.<anonymous>' call\n tmp = bitMaskWith(iid);\n }\n var iidImask = tmp;\n if (!(iidImask == null)) {\n masks.push(iidImask);\n currentSize = Math.max(currentSize, iidImask.length);\n }\n if (currentSize > maxSize) {\n maxSize = currentSize;\n }\n }\n return compositeBitMask(maxSize, masks);\n }\n function bitMaskWith(activeBit) {\n var numberIndex = activeBit >> 5;\n var intArray = new Int32Array(numberIndex + 1 | 0);\n var positionInNumber = activeBit & 31;\n var numberWithSettledBit = 1 << positionInNumber;\n intArray[numberIndex] = intArray[numberIndex] | numberWithSettledBit;\n return intArray;\n }\n function compositeBitMask(capacity, masks) {\n var tmp = 0;\n var tmp_0 = new Int32Array(capacity);\n while (tmp < capacity) {\n var tmp_1 = tmp;\n var result = 0;\n var inductionVariable = 0;\n var last = masks.length;\n while (inductionVariable < last) {\n var mask = masks[inductionVariable];\n inductionVariable = inductionVariable + 1 | 0;\n if (tmp_1 < mask.length) {\n result = result | mask[tmp_1];\n }\n }\n tmp_0[tmp_1] = result;\n tmp = tmp + 1 | 0;\n }\n return tmp_0;\n }\n function protoOf(constructor) {\n return constructor.prototype;\n }\n function defineProp(obj, name, getter, setter) {\n return Object.defineProperty(obj, name, {configurable: true, get: getter, set: setter});\n }\n function objectCreate(proto) {\n return Object.create(proto);\n }\n function classMeta(name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity) {\n return createMetadata('class', name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, null);\n }\n function createMetadata(kind, name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, iid) {\n var undef = VOID;\n return {kind: kind, simpleName: name, associatedObjectKey: associatedObjectKey, associatedObjects: associatedObjects, suspendArity: suspendArity, $kClass$: undef, defaultConstructor: defaultConstructor, iid: iid};\n }\n function setMetadataFor(ctor, name, metadataConstructor, parent, interfaces, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity) {\n if (!(parent == null)) {\n ctor.prototype = Object.create(parent.prototype);\n ctor.prototype.constructor = ctor;\n }\n var metadata = metadataConstructor(name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity == null ? [] : suspendArity);\n ctor.$metadata$ = metadata;\n if (!(interfaces == null)) {\n var receiver = !(metadata.iid == null) ? ctor : ctor.prototype;\n receiver.$imask$ = implement(interfaces);\n }\n }\n function objectMeta(name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity) {\n return createMetadata('object', name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, null);\n }\n function get_VOID() {\n _init_properties_void_kt__3zg9as();\n return VOID;\n }\n var VOID;\n var properties_initialized_void_kt_e4ret2;\n function _init_properties_void_kt__3zg9as() {\n if (!properties_initialized_void_kt_e4ret2) {\n properties_initialized_void_kt_e4ret2 = true;\n VOID = void 0;\n }\n }\n function main() {\n print('${text}');\n }\n //region block: init\n Unit_instance = new Unit();\n //endregion\nif (typeof get_output !== "undefined") {\n get_output();\n output = new BufferedOutput();\n _.output = get_output();\n}\n main();\n return _;\n}(typeof moduleId === 'undefined' ? {} : moduleId);\nmoduleId.output?.buffer_1;\n\n` as const;
3+
}

tests/utlis/mocks/wasm.json

Lines changed: 11 additions & 0 deletions
Large diffs are not rendered by default.

yarn.lock

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,15 +1182,12 @@
11821182
picocolors "^1.0.0"
11831183
tslib "^2.6.0"
11841184

1185-
"@playwright/test@^1.37.1":
1186-
version "1.37.1"
1187-
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.37.1.tgz#e7f44ae0faf1be52d6360c6bbf689fd0057d9b6f"
1188-
integrity sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg==
1185+
"@playwright/test@^1.40.1":
1186+
version "1.40.1"
1187+
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.40.1.tgz#9e66322d97b1d74b9f8718bacab15080f24cde65"
1188+
integrity sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==
11891189
dependencies:
1190-
"@types/node" "*"
1191-
playwright-core "1.37.1"
1192-
optionalDependencies:
1193-
fsevents "2.3.2"
1190+
playwright "1.40.1"
11941191

11951192
"@types/body-parser@*":
11961193
version "1.19.2"
@@ -6081,10 +6078,19 @@ pkg-dir@^7.0.0:
60816078
dependencies:
60826079
find-up "^6.3.0"
60836080

6084-
playwright-core@1.37.1:
6085-
version "1.37.1"
6086-
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.37.1.tgz#cb517d52e2e8cb4fa71957639f1cd105d1683126"
6087-
integrity sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==
6081+
playwright-core@1.40.1:
6082+
version "1.40.1"
6083+
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.40.1.tgz#442d15e86866a87d90d07af528e0afabe4c75c05"
6084+
integrity sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==
6085+
6086+
playwright@1.40.1:
6087+
version "1.40.1"
6088+
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.40.1.tgz#a11bf8dca15be5a194851dbbf3df235b9f53d7ae"
6089+
integrity sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==
6090+
dependencies:
6091+
playwright-core "1.40.1"
6092+
optionalDependencies:
6093+
fsevents "2.3.2"
60886094

60896095
pluralize@5.0.0:
60906096
version "5.0.0"

0 commit comments

Comments
 (0)