1
0

smoke.test.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. //@ts-check
  6. const playwright = require('playwright');
  7. const { assert } = require('chai');
  8. /** @typedef {import('./common').BrowserKind} BrowserKind */
  9. /** @typedef {import('./common').PackagerKind} PackagerKind */
  10. /** @typedef {import('./common').TestInfo} TestInfo */
  11. /** @type TestInfo */
  12. const testInfo = JSON.parse(process.env.MONACO_TEST_INFO || '');
  13. const URLS = {
  14. amd: `http://127.0.0.1:${testInfo.port}/test/smoke/amd/index.html`,
  15. webpack: `http://127.0.0.1:${testInfo.port}/test/smoke/webpack/index.html`,
  16. esbuild: `http://127.0.0.1:${testInfo.port}/test/smoke/esbuild/index.html`,
  17. vite: `http://127.0.0.1:${testInfo.port}/test/smoke/vite/dist/index.html`,
  18. parcel: `http://127.0.0.1:${testInfo.port}/test/smoke/parcel/dist/index.html`
  19. };
  20. const URL = URLS[testInfo.packager];
  21. suite(`Smoke Test '${testInfo.packager}' on '${testInfo.browser}'`, () => {
  22. /** @type {playwright.Browser} */
  23. let browser;
  24. /** @type {playwright.Page} */
  25. let page;
  26. suiteSetup(async () => {
  27. browser = await playwright[testInfo.browser].launch({
  28. headless: !testInfo.debugTests,
  29. devtools: testInfo.debugTests && testInfo.browser === 'chromium'
  30. // slowMo: testInfo.debugTests ? 2000 : 0
  31. });
  32. });
  33. suiteTeardown(async () => {
  34. await browser.close();
  35. });
  36. let pageErrors = [];
  37. setup(async () => {
  38. pageErrors = [];
  39. page = await browser.newPage({
  40. viewport: {
  41. width: 800,
  42. height: 600
  43. }
  44. });
  45. page.on('pageerror', (e) => {
  46. console.log(e);
  47. pageErrors.push(e);
  48. });
  49. const response = await page.goto(URL);
  50. if (!response) {
  51. assert.fail('Failed to load page');
  52. }
  53. assert.strictEqual(response.status(), 200);
  54. });
  55. teardown(async () => {
  56. for (const e of pageErrors) {
  57. throw e;
  58. }
  59. await page.close();
  60. });
  61. /**
  62. * @param {string} text
  63. * @param {string} language
  64. * @returns Promise<void>
  65. */
  66. async function createEditor(text, language) {
  67. return await page.evaluate(
  68. `window.ed = monacoAPI.editor.create(document.getElementById('editor-container'), { value: '${text}', language: '${language}' })`
  69. );
  70. }
  71. /**
  72. * @param {number} lineNumber
  73. * @param {number} column
  74. * @returns Promise<void>
  75. */
  76. async function setEditorPosition(lineNumber, column) {
  77. return await page.evaluate(
  78. `window.ed.setPosition({ lineNumber: ${lineNumber}, column: ${column} });`
  79. );
  80. }
  81. /**
  82. * @param {string} commandId
  83. * @param {any} [args]
  84. * @returns Promise<void>
  85. */
  86. async function triggerEditorCommand(commandId, args) {
  87. return await page.evaluate(
  88. `window.ed.trigger(null, '${commandId}', ${args ? JSON.stringify(args) : 'undefined'});`
  89. );
  90. }
  91. async function focusEditor() {
  92. await page.evaluate(`window.ed.focus();`);
  93. }
  94. test('`monacoAPI` is exposed as global', async function () {
  95. assert.strictEqual(await page.evaluate(`typeof monacoAPI`), 'object');
  96. });
  97. test('should be able to create plaintext editor', async function () {
  98. await createEditor('hello world', 'plaintext');
  99. // type a link in it
  100. await setEditorPosition(1, 12);
  101. await triggerEditorCommand('type', { text: '\nhttps://www.microsoft.com' });
  102. // check that the link gets highlighted, which indicates that the web worker is healthy
  103. await page.waitForSelector('.detected-link');
  104. });
  105. test('css smoke test', async function () {
  106. await createEditor('.sel1 { background: red; }\\n.sel2 {}', 'css');
  107. // check that a squiggle appears, which indicates that the language service is up and running
  108. await page.waitForSelector('.squiggly-warning');
  109. });
  110. test('html smoke test', async function () {
  111. await createEditor('<title>hi</title>', 'html');
  112. // we need to try this a couple of times because the web worker might not be ready yet
  113. for (let attempt = 1; attempt <= 2; attempt++) {
  114. // trigger hover
  115. await focusEditor();
  116. await setEditorPosition(1, 3);
  117. await page.keyboard.press('F1');
  118. await page.keyboard.type('Show Hover');
  119. await page.keyboard.press('Enter');
  120. // check that a hover explaining the `<title>` element appears, which indicates that the language service is up and running
  121. try {
  122. await page.waitForSelector(
  123. `text=The title element represents the document's title or name`,
  124. { timeout: 5000 }
  125. );
  126. } catch (err) {}
  127. }
  128. });
  129. test('json smoke test', async function () {
  130. await createEditor('{}', 'json');
  131. // we need to try this a couple of times because the web worker might not be ready yet
  132. for (let attempt = 1; attempt <= 2; attempt++) {
  133. // trigger suggestions
  134. await focusEditor();
  135. await setEditorPosition(1, 2);
  136. await triggerEditorCommand('editor.action.triggerSuggest');
  137. // check that a suggestion item for `$schema` appears, which indicates that the language service is up and running
  138. try {
  139. await page.waitForSelector(`text=$schema`, { timeout: 5000 });
  140. } catch (err) {}
  141. }
  142. });
  143. test('typescript smoke test', async function () {
  144. await createEditor('window.add', 'typescript');
  145. // check that a squiggle appears, which indicates that the language service is up and running
  146. await page.waitForSelector('.squiggly-error');
  147. // at this point we know that the web worker is healthy, so we can trigger suggestions
  148. // trigger suggestions
  149. await focusEditor();
  150. await setEditorPosition(1, 11);
  151. await triggerEditorCommand('editor.action.triggerSuggest');
  152. // check that a suggestion item for `addEventListener` appears, which indicates that the language service is up and running
  153. await page.waitForSelector(`text=addEventListener`);
  154. // find the TypeScript worker
  155. function findAsync(arr, fn) {
  156. return Promise.all(arr.map(fn)).then((results) => {
  157. return arr.find((_, i) => results[i]);
  158. });
  159. }
  160. // check that the TypeScript worker exposes `ts` as a global
  161. const tsWorker = await findAsync(
  162. page.workers(),
  163. async (page) => await page.evaluate(`typeof ts !== 'undefined'`)
  164. );
  165. if (!tsWorker) {
  166. assert.fail('Could not find TypeScript worker');
  167. }
  168. // check that the TypeScript worker exposes the full `ts` as a global
  169. assert.strictEqual(await tsWorker.evaluate(`typeof ts.optionDeclarations`), 'object');
  170. });
  171. });
  172. function timeout(ms) {
  173. return new Promise((resolve, reject) => {
  174. setTimeout(resolve, ms);
  175. });
  176. }