From 9945754a6203ab0e92a7f77b9a18e63c0f9392bd Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 28 Dec 2020 21:43:35 +0100 Subject: [PATCH] Add editor smoke test (ported from `monaco-editor`) --- .eslintrc.json | 7 ++ .github/workflows/ci.yml | 15 ++++ build/hygiene.js | 1 + build/npm/dirs.js | 1 + test/monaco/.gitignore | 2 + test/monaco/.mocharc.json | 5 ++ test/monaco/README.md | 13 +++ test/monaco/core.js | 30 +++++++ test/monaco/dist/core.html | 10 +++ test/monaco/monaco.test.ts | 147 ++++++++++++++++++++++++++++++++++ test/monaco/package.json | 16 ++++ test/monaco/runner.js | 52 ++++++++++++ test/monaco/tsconfig.json | 21 +++++ test/monaco/webpack.config.js | 55 +++++++++++++ test/monaco/yarn.lock | 52 ++++++++++++ 15 files changed, 427 insertions(+) create mode 100644 test/monaco/.gitignore create mode 100644 test/monaco/.mocharc.json create mode 100644 test/monaco/README.md create mode 100644 test/monaco/core.js create mode 100644 test/monaco/dist/core.html create mode 100644 test/monaco/monaco.test.ts create mode 100644 test/monaco/package.json create mode 100644 test/monaco/runner.js create mode 100644 test/monaco/tsconfig.json create mode 100644 test/monaco/webpack.config.js create mode 100644 test/monaco/yarn.lock diff --git a/.eslintrc.json b/.eslintrc.json index fd72f0e58f1..a2e1351c76b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -898,6 +898,13 @@ "*" // node modules ] }, + { + "target": "**/test/monaco/**", + "restrictions": [ + "**/test/monaco/**", + "*" // node modules + ] + }, { "target": "**/api/**.test.ts", "restrictions": [ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 666f9f6c84d..bb4b9dcfbe1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -288,3 +288,18 @@ jobs: ../node_modules/.bin/tsc --init echo "import '../out-monaco-editor-core';" > a.ts ../node_modules/.bin/tsc --noEmit + + - name: Webpack Editor + working-directory: ./test/monaco + run: yarn run bundle + + - name: Compile Editor Tests + working-directory: ./test/monaco + run: yarn run compile + + - name: Download Playwright + run: yarn playwright-install + + - name: Run Editor Tests + working-directory: ./test/monaco + run: yarn test diff --git a/build/hygiene.js b/build/hygiene.js index a08019f3075..d874f308cc8 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -55,6 +55,7 @@ const indentationFilter = [ // except specific folders '!test/automation/out/**', + '!test/monaco/out/**', '!test/smoke/out/**', '!extensions/typescript-language-features/test-workspace/**', '!extensions/vscode-api-tests/testWorkspace/**', diff --git a/build/npm/dirs.js b/build/npm/dirs.js index a6e2aec4067..f3d7ef7c61a 100644 --- a/build/npm/dirs.js +++ b/build/npm/dirs.js @@ -44,5 +44,6 @@ exports.dirs = [ 'remote/web', 'test/automation', 'test/integration/browser', + 'test/monaco', 'test/smoke', ]; diff --git a/test/monaco/.gitignore b/test/monaco/.gitignore new file mode 100644 index 00000000000..21786a84195 --- /dev/null +++ b/test/monaco/.gitignore @@ -0,0 +1,2 @@ +dist/**/*.js +dist/**/*.ttf diff --git a/test/monaco/.mocharc.json b/test/monaco/.mocharc.json new file mode 100644 index 00000000000..fd9de38be2e --- /dev/null +++ b/test/monaco/.mocharc.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/mocharc", + "ui": "bdd", + "timeout": 10000 +} diff --git a/test/monaco/README.md b/test/monaco/README.md new file mode 100644 index 00000000000..68bb7051ce8 --- /dev/null +++ b/test/monaco/README.md @@ -0,0 +1,13 @@ +# Monaco Editor Test + +This directory contains scripts that are used to smoke test the Monaco Editor distribution. + +## Setup & Bundle + + $test/monaco> yarn + $test/monaco> yarn run bundle + +## Compile and run tests + + $test/monaco> yarn run compile + $test/monaco> yarn test diff --git a/test/monaco/core.js b/test/monaco/core.js new file mode 100644 index 00000000000..bd1fe4ac4cc --- /dev/null +++ b/test/monaco/core.js @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as monaco from 'monaco-editor-core'; + +self.MonacoEnvironment = { + getWorkerUrl: function (moduleId, label) { + return './editor.worker.bundle.js'; + } +} + +window.instance = monaco.editor.create(document.getElementById('container'), { + value: [ + 'from banana import *', + '', + 'class Monkey:', + ' # Bananas the monkey can eat.', + ' capacity = 10', + ' def eat(self, N):', + ' \'\'\'Make the monkey eat N bananas!\'\'\'', + ' capacity = capacity - N*banana.size', + '', + ' def feeding_frenzy(self):', + ' eat(9.25)', + ' return "Yum yum"', + ].join('\n'), + language: 'python' +}); diff --git a/test/monaco/dist/core.html b/test/monaco/dist/core.html new file mode 100644 index 00000000000..35f656becf1 --- /dev/null +++ b/test/monaco/dist/core.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/test/monaco/monaco.test.ts b/test/monaco/monaco.test.ts new file mode 100644 index 00000000000..612d58c527f --- /dev/null +++ b/test/monaco/monaco.test.ts @@ -0,0 +1,147 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as playwright from 'playwright'; +import { assert } from 'chai'; + +const PORT = 8563; + +const APP = `http://127.0.0.1:${PORT}/dist/core.html`; + +let browser: playwright.Browser; +let page: playwright.Page; + +type BrowserType = 'chromium' | 'firefox' | 'webkit'; + +const browserType: BrowserType = process.env.BROWSER as BrowserType || 'chromium'; + +before(async function () { + this.timeout(5 * 1000); + console.log(`Starting browser: ${browserType}`); + browser = await playwright[browserType].launch({ + headless: process.argv.includes('--headless'), + }); +}); + +after(async function () { + this.timeout(5 * 1000); + await browser.close(); +}); + +beforeEach(async function () { + this.timeout(5 * 1000); + page = await browser.newPage({ + viewport: { + width: 800, + height: 600 + } + }); +}); + +afterEach(async () => { + await page.close(); +}); + +describe('Basic loading', function (): void { + this.timeout(20000); + + it('should fail because page has an error', async () => { + const pageErrors: any[] = []; + page.on('pageerror', (e) => { + console.log(e); + pageErrors.push(e); + }); + + page.on('pageerror', (e) => { + console.log(e); + pageErrors.push(e); + }); + + await page.goto(APP); + this.timeout(20000); + + for (const e of pageErrors) { + throw e; + } + }); +}); + +describe('API Integration Tests', function (): void { + this.timeout(20000); + + beforeEach(async () => { + await page.goto(APP); + }); + + it('Default initialization should be error-less', async function (): Promise { + assert.equal(await page.evaluate(`monaco.editor.DefaultEndOfLine[1]`), 'LF'); + }); + + it('Focus and Type', async function (): Promise { + await page.evaluate(` + (function () { + instance.focus(); + instance.trigger('keyboard', 'cursorHome'); + instance.trigger('keyboard', 'type', { + text: 'a' + }); + })() + `); + assert.equal(await page.evaluate(`instance.getModel().getLineContent(1)`), 'afrom banana import *'); + }); + + it('Type and Undo', async function (): Promise { + await page.evaluate(` + (function () { + instance.focus(); + instance.trigger('keyboard', 'cursorHome'); + instance.trigger('keyboard', 'type', { + text: 'a' + }); + instance.getModel().undo(); + })() + `); + assert.equal(await page.evaluate(`instance.getModel().getLineContent(1)`), 'from banana import *'); + }); + + it('Multi Cursor', async function (): Promise { + await page.evaluate(` + (function () { + instance.focus(); + instance.trigger('keyboard', 'editor.action.insertCursorBelow'); + instance.trigger('keyboard', 'editor.action.insertCursorBelow'); + instance.trigger('keyboard', 'editor.action.insertCursorBelow'); + instance.trigger('keyboard', 'editor.action.insertCursorBelow'); + instance.trigger('keyboard', 'editor.action.insertCursorBelow'); + instance.trigger('keyboard', 'type', { + text: '# ' + }); + instance.focus(); + })() + `); + + await page.waitForTimeout(1000); + + assert.deepEqual(await page.evaluate(` + [ + instance.getModel().getLineContent(1), + instance.getModel().getLineContent(2), + instance.getModel().getLineContent(3), + instance.getModel().getLineContent(4), + instance.getModel().getLineContent(5), + instance.getModel().getLineContent(6), + instance.getModel().getLineContent(7), + ] + `), [ + '# from banana import *', + '# ', + '# class Monkey:', + '# # Bananas the monkey can eat.', + '# capacity = 10', + '# def eat(self, N):', + '\t\t\'\'\'Make the monkey eat N bananas!\'\'\'' + ]); + }); +}); diff --git a/test/monaco/package.json b/test/monaco/package.json new file mode 100644 index 00000000000..872fde3faca --- /dev/null +++ b/test/monaco/package.json @@ -0,0 +1,16 @@ +{ + "name": "test-monaco", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "private": true, + "scripts": { + "compile": "node ../../node_modules/typescript/bin/tsc", + "bundle": "node ../../node_modules/webpack/bin/webpack --config ./webpack.config.js --bail", + "test": "node runner.js" + }, + "devDependencies": { + "@types/chai": "^4.2.14", + "chai": "^4.2.0" + } +} diff --git a/test/monaco/runner.js b/test/monaco/runner.js new file mode 100644 index 00000000000..c2f90d3f1a7 --- /dev/null +++ b/test/monaco/runner.js @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const yaserver = require('yaserver'); +const http = require('http'); +const cp = require('child_process'); + +const PORT = 8563; + +yaserver.createServer({ + rootDir: __dirname +}).then((staticServer) => { + const server = http.createServer((request, response) => { + return staticServer.handle(request, response); + }); + server.listen(PORT, '127.0.0.1', () => { + runTests().then(() => { + console.log(`All good`); + process.exit(0); + }, (err) => { + console.error(err); + process.exit(1); + }) + }); +}); + +function runTests() { + return ( + runTest('chromium') + .then(() => runTest('firefox')) + .then(() => runTest('webkit')) + ); +} + +function runTest(browser) { + return new Promise((resolve, reject) => { + const proc = cp.spawn('node', ['../../node_modules/mocha/bin/mocha', 'out/*.test.js', '--headless'], { + env: { BROWSER: browser, ...process.env }, + stdio: 'inherit' + }); + proc.on('error', reject); + proc.on('exit', (code) => { + if (code === 0) { + resolve(); + } else { + reject(code); + } + }); + }) +} diff --git a/test/monaco/tsconfig.json b/test/monaco/tsconfig.json new file mode 100644 index 00000000000..24deb99ddf8 --- /dev/null +++ b/test/monaco/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2016", + "strict": true, + "noUnusedParameters": false, + "noUnusedLocals": true, + "outDir": "out", + "sourceMap": true, + "declaration": true, + "lib": [ + "es2016", + "dom" + ] + }, + "exclude": [ + "node_modules", + "out", + "tools" + ] +} diff --git a/test/monaco/webpack.config.js b/test/monaco/webpack.config.js new file mode 100644 index 00000000000..5e7e9c5f643 --- /dev/null +++ b/test/monaco/webpack.config.js @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const path = require('path'); + +module.exports = { + mode: 'production', + entry: { + "core": './core.js', + "editor.worker": '../../out-monaco-editor-core/esm/vs/editor/editor.worker.js', + }, + output: { + globalObject: 'self', + filename: '[name].bundle.js', + path: path.resolve(__dirname, './dist') + }, + module: { + rules: [ + { + test: /\.css$/, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, + use: [ + { + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'fonts/' + } + } + ] + } + ] + }, + resolve: { + alias: { + 'monaco-editor-core/esm/vs/editor/editor.worker': path.resolve(__dirname, '../../out-monaco-editor-core/esm/vs/editor/editor.worker.js'), + 'monaco-editor-core': path.resolve(__dirname, '../../out-monaco-editor-core/esm/vs/editor/editor.main.js'), + } + }, + stats: { + all: false, + modules: true, + errors: true, + warnings: true, + // our additional options + moduleTrace: true, + errorDetails: true, + chunks: true + } +}; diff --git a/test/monaco/yarn.lock b/test/monaco/yarn.lock new file mode 100644 index 00000000000..f61353d112e --- /dev/null +++ b/test/monaco/yarn.lock @@ -0,0 +1,52 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/chai@^4.2.14": + version "4.2.14" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.14.tgz#44d2dd0b5de6185089375d976b4ec5caf6861193" + integrity sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ== + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +chai@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==