Skip to content

Commit

Permalink
Fix launching extension in safari (#10908)
Browse files Browse the repository at this point in the history
* Ensure the extension loads in the `Safari` browser

* Misc

* Misc

* Fixes

* Misc

* Misc

* oops

* Inclue dependency

* top

* oops
  • Loading branch information
DonJayamanne authored Jul 25, 2022
1 parent 98d802e commit ca46927
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 63 deletions.
15 changes: 14 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@
"problemMatcher": [],
"label": "preTestJediLSP"
},
{
"type": "npm",
"script": "launchWebExtension",
"problemMatcher": [],
"label": "Launch Web Extension (Chrome)"
},
{
"type": "shell",
"problemMatcher": [],
"command": "npm",
"args": ["run", "launchWebExtension", "--", "--browser=webkit", "--port=3111"],
"label": "Launch Web Extension (Safari)"
},
{
"type": "npm",
"script": "lint",
Expand All @@ -88,7 +101,7 @@
"options": {
"env": {
"CI_PYTHON_PATH": "", // Update with path to real python interpereter used for testing.
"EXISTING_JUPYTER_URI": "", // Update with a server that you started yourself to avoid this script doing it
"EXISTING_JUPYTER_URI": "" // Update with a server that you started yourself to avoid this script doing it
}
}
},
Expand Down
23 changes: 22 additions & 1 deletion build/ci/postInstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const colors = require('colors/safe');
const fs = require('fs-extra');
const path = require('path');
const constants = require('../constants');
const child_process = require('child_process');

/**
* In order to get raw kernels working, we reuse the default kernel that jupyterlab ships.
Expand Down Expand Up @@ -125,7 +124,29 @@ function fixJupyterLabRenderers() {
}
}

/**
* Ensures extension loads in safari (https://github.com/microsoft/vscode-jupyter/issues/10621)
* Some of the regexes are not supported in safari and not required either.
*/
function fixStripComments() {
const file = 'node_modules/strip-comments/lib/languages.js';
const filePath = path.join(__dirname, '..', '..', file);
if (!fs.existsSync(filePath)) {
return;
}
const contents = `
'use strict';
exports.javascript = {
BLOCK_OPEN_REGEX: /^\\/\\*\\*?(!?)/,
BLOCK_CLOSE_REGEX: /^\\*\\/(\\n?)/,
LINE_REGEX: /^\\/\\/(!?).*/
};`;
fs.writeFileSync(filePath, contents);
}

fixJupyterLabRenderers();
makeVariableExplorerAlwaysSorted();
createJupyterKernelWithoutSerialization();
updateJSDomTypeDefinition();
fixStripComments();
6 changes: 6 additions & 0 deletions build/launchWeb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

const { launch } = require('./launchWebUtils');

void launch();
58 changes: 2 additions & 56 deletions build/launchWebTest.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

const path = require('path');
const fs = require('fs-extra');
const test_web = require('@vscode/test-web');
const { startJupyter } = require('./preLaunchWebTest');
const jsonc = require('jsonc-parser');
const { startReportServer } = require('./webTestReporter');
const { noop } = require('../out/test/core');
const { isCI } = require('./constants');
const extensionDevelopmentPath = path.resolve(__dirname, '../');
const packageJsonFile = path.join(extensionDevelopmentPath, 'package.json');
const { launch } = require('./launchWebUtils');

async function go() {
let exitCode = 0;
let server;
let testServer;
try {
server = (await startJupyter()).server;
testServer = await startReportServer();
const bundlePath = path.join(extensionDevelopmentPath, 'out', 'extension.web.bundle');

// Changing the logging level to be read from workspace settings file.
// This way we can enable verbose logging and get the logs for web tests.
const settingsJson = fs.readFileSync(packageJsonFile).toString();
const edits = jsonc.modify(
settingsJson,
['contributes', 'configuration', 'properties', 'jupyter.logging.level', 'scope'],
'resource',
{}
);
const updatedSettingsJson = jsonc.applyEdits(settingsJson, edits);
fs.writeFileSync(packageJsonFile, updatedSettingsJson);

// Now run the test
await test_web.runTests({
browserType: 'chromium',
verbose: true,
headless: isCI ? false : true, // Set this to false to debug failures (false on CI to support capturing screenshots when tests fail).
extensionDevelopmentPath,
folderPath: path.resolve(__dirname, '..', 'src', 'test', 'datascience'),
extensionTestsPath: bundlePath
});
} catch (err) {
console.error('Failed to run tests', err);
exitCode = 1;
} finally {
if (testServer) {
await testServer.dispose().catch(noop);
}
if (server) {
await server.dispose();
}
}

// Not all promises complete. Force exit
process.exit(exitCode);
}
void go();
void launch(true);
69 changes: 69 additions & 0 deletions build/launchWebUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

const path = require('path');
const fs = require('fs-extra');
const test_web = require('@vscode/test-web');
const { startJupyter } = require('./preLaunchWebTest');
const jsonc = require('jsonc-parser');
const { startReportServer } = require('./webTestReporter');
const { noop } = require('../out/test/core');
const { isCI } = require('./constants');
const extensionDevelopmentPath = path.resolve(__dirname, '../');
const packageJsonFile = path.join(extensionDevelopmentPath, 'package.json');
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const argv = yargs(hideBin(process.argv)).argv;

const browserType = argv.browser || argv.browserType || 'chromium';
const port = argv.port || 3000;

exports.launch = async function launch(launchTests) {
let exitCode = 0;
let server;
let testServer;
try {
if (launchTests) {
server = (await startJupyter()).server;
testServer = await startReportServer();
}
const bundlePath = path.join(extensionDevelopmentPath, 'out', 'extension.web.bundle');

// Changing the logging level to be read from workspace settings file.
// This way we can enable verbose logging and get the logs for web tests.
const settingsJson = fs.readFileSync(packageJsonFile).toString();
const edits = jsonc.modify(
settingsJson,
['contributes', 'configuration', 'properties', 'jupyter.logging.level', 'scope'],
'resource',
{}
);
const updatedSettingsJson = jsonc.applyEdits(settingsJson, edits);
fs.writeFileSync(packageJsonFile, updatedSettingsJson);
const options = {
browserType,
verbose: true,
port,
headless: isCI ? false : false, // Set this to false to debug failures (false on CI to support capturing screenshots when tests fail).
extensionDevelopmentPath,
folderPath: path.resolve(__dirname, '..', 'src', 'test', 'datascience')
};
if (launchTests) {
options.extensionTestsPath = bundlePath;
}
await test_web.runTests(options);
} catch (err) {
console.error(launchTests ? 'Failed to run tests' : 'Failed to launch VS Code', err);
exitCode = 1;
} finally {
if (testServer) {
await testServer.dispose().catch(noop);
}
if (server) {
await server.dispose();
}
}

// Not all promises complete. Force exit
process.exit(exitCode);
};
1 change: 1 addition & 0 deletions news/2 Fixes/10621.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure the extension loads in the `Safari` browser.
7 changes: 3 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2195,6 +2195,7 @@
"testNativeNotebooksWithoutPythonInVSCode": "cross-env CODE_TESTS_WORKSPACE=src/test/datascience VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders TEST_FILES_SUFFIX=*.vscode.test,*.vscode.common.test VSC_JUPYTER_FORCE_LOGGING=1 VSC_JUPYTER_LOAD_EXPERIMENTS_FROM_FILE=true VSC_JUPYTER_CI_TEST_GREP=non-python-kernel VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT=true node ./out/test/testBootstrap.node.js ./out/test/standardTest.node.js",
"testNativeNotebooksAndWebviews": "cross-env CODE_TESTS_WORKSPACE=src/test/datascience VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders TEST_FILES_SUFFIX=*.vscode.test,*.vscode.common.test VSC_JUPYTER_FORCE_LOGGING=1 VSC_JUPYTER_CI_TEST_GREP=webview-test VSC_JUPYTER_LOAD_EXPERIMENTS_FROM_FILE=true node ./out/test/testBootstrap.node.js ./out/test/standardTest.node.js",
"testWebExtension": "node ./build/launchWebTest.js",
"launchWebExtension": "node ./build/launchWeb.js",
"testPerformance": "node ./out/test/perfTest.node.js",
"testSmoke": "node ./out/test/testBootstrap.node.js ./out/test/smokeTest.node.js",
"testSmokeLogged": "cross-env VSC_JUPYTER_FORCE_LOGGING=true VSC_JUPYTER_LOG_FILE=smoke-test.log node --no-force-async-hooks-checks ./out/test/testBootstrap.node.js ./out/test/smokeTest.node.js",
Expand Down Expand Up @@ -2273,6 +2274,7 @@
"safe-buffer": "^5.2.1",
"sanitize-filename": "^1.6.3",
"semver": "^5.5.0",
"setimmediate": "^1.0.5",
"slickgrid": "^2.4.17",
"source-map": "^0.7.3",
"stack-trace": "0.0.10",
Expand Down
7 changes: 7 additions & 0 deletions src/extension.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ if ((Reflect as any).metadata === undefined) {
require('reflect-metadata');
}

// Polly fill for webworkers in safari,
// The scripts load in chrome because chrome supports offScreenCanvas which in turn supports requestAnimationFrame,
// & requestAnimationFrame is the preferred approach and setImmediate is the fallback.
// As requestAnimationFrame is supported in chrome webworkers there's no need for a fallback to setImmediate.
// https://github.com/microsoft/vscode-jupyter/issues/10621
require('setimmediate');

// Initialize the logger first.
require('./platform/logging');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function extractRequireConfigFromWidgetEntry(baseUrl: Uri, widgetFolderNa
// the config entry is js, and not json.
// We cannot eval as thats dangerous, and we cannot use JSON.parse either as it not JSON.
// Lets just extract what we need.
configStr = stripComments(configStr);
configStr = stripComments(configStr, { language: 'javascript' });
configStr = configStr.splitLines({ trim: true, removeEmptyEntries: true }).join('');
// Now that we have just valid JS, extract contents between the third '{' and corresponding ending '}'
const mappings = configStr
Expand Down

0 comments on commit ca46927

Please sign in to comment.