summaryrefslogtreecommitdiff
path: root/tools/test-helper
diff options
context:
space:
mode:
authorLeedehai <18319900+Leedehai@users.noreply.github.com>2023-12-20 06:03:09 -0500
committerGitHub <noreply@github.com>2023-12-20 12:03:09 +0100
commit6eb6e877ae49eab6a2baaa994da6e36ae74d6a32 (patch)
treefefaee8fc52ead6bdbe85254dbde2fcb8c8161c5 /tools/test-helper
parent3dc4eb6bcd78afb379736ca4c0517f8de27a10ec (diff)
Improve test-helper (#2820)
Diffstat (limited to 'tools/test-helper')
-rw-r--r--tools/test-helper/extension.js246
-rw-r--r--tools/test-helper/package.json114
2 files changed, 265 insertions, 95 deletions
diff --git a/tools/test-helper/extension.js b/tools/test-helper/extension.js
index 4682505c..60bfe982 100644
--- a/tools/test-helper/extension.js
+++ b/tools/test-helper/extension.js
@@ -1,87 +1,225 @@
const vscode = require('vscode')
const cp = require('child_process')
-function activate(context) {
- let panel = null
+class TestHelper {
+ constructor() {
+ /** @type {vscode.Uri?} */ this.sourceUriOfActivePanel = null
+ /** @type {Map<vscode.Uri, vscode.WebviewPanel>} */ this.panels = new Map()
+
+ /** @type {vscode.StatusBarItem} */ this.testRunningStatusBarItem =
+ vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right)
+ this.testRunningStatusBarItem.text = "$(loading~spin) Running"
+ this.testRunningStatusBarItem.backgroundColor =
+ new vscode.ThemeColor('statusBarItem.warningBackground')
+ }
+
+ /**
+ * The caller should ensure when this function is called, a text editor is active.
+ * Note the fake "editor" for the extension's WebView panel is not one.
+ * @returns {vscode.Uri}
+ */
+ static getActiveDocumentUri() {
+ const editor = vscode.window.activeTextEditor
+ if (!editor) {
+ throw new Error('vscode.window.activeTextEditor is undefined.')
+ }
+ return editor.document.uri
+ }
+
+ /**
+ * The caller should ensure when this function is called, a WebView panel is active.
+ * @returns {vscode.Uri}
+ */
+ getSourceUriOfActivePanel() {
+ // If this function is invoked when user clicks the button from within a WebView
+ // panel, then the active panel is this panel, and sourceUriOfActivePanel is
+ // guaranteed to have been updated by that panel's onDidChangeViewState listener.
+ if (!this.sourceUriOfActivePanel) {
+ throw new Error('sourceUriOfActivePanel is falsy; is there a focused panel?')
+ }
+ return this.sourceUriOfActivePanel
+ }
- function refreshPanel(stdout, stderr) {
- const uri = vscode.window.activeTextEditor.document.uri
- const { pngPath, refPath } = getPaths(uri)
+ /** @param {vscode.Uri} uri */
+ static getImageUris(uri) {
+ const png = vscode.Uri.file(uri.path
+ .replace("tests/typ", "tests/png")
+ .replace(".typ", ".png"))
+ const ref = vscode.Uri.file(uri.path
+ .replace("tests/typ", "tests/ref")
+ .replace(".typ", ".png"))
+
+ return {png, ref}
+ }
+
+ /**
+ * @param {vscode.Uri} uri
+ * @param {string} stdout
+ * @param {string} stderr
+ */
+ refreshTestPreviewImpl_(uri, stdout, stderr) {
+ const {png, ref} = TestHelper.getImageUris(uri)
+
+ const panel = this.panels.get(uri)
if (panel && panel.visible) {
- console.log('Refreshing WebView')
- const pngSrc = panel.webview.asWebviewUri(pngPath)
- const refSrc = panel.webview.asWebviewUri(refPath)
+ console.log(`Refreshing WebView for ${uri.fsPath}`)
+ const webViewSrcs = {
+ png: panel.webview.asWebviewUri(png),
+ ref: panel.webview.asWebviewUri(ref),
+ }
panel.webview.html = ''
// Make refresh notable.
setTimeout(() => {
- panel.webview.html = getWebviewContent(pngSrc, refSrc, stdout, stderr)
+ if (!panel) {
+ throw new Error('panel to refresh is falsy after waiting')
+ }
+ panel.webview.html = getWebviewContent(webViewSrcs, stdout, stderr)
}, 50)
}
}
- const openCmd = vscode.commands.registerCommand("ShortcutMenuBar.testOpen", () => {
- panel = vscode.window.createWebviewPanel(
- 'testOutput',
- 'Test output',
+ /** @param {vscode.Uri} uri */
+ openTestPreview(uri) {
+ if (this.panels.has(uri)) {
+ this.panels.get(uri)?.reveal()
+ return
+ }
+
+ const newPanel = vscode.window.createWebviewPanel(
+ 'Typst.test-helper.preview',
+ uri.path.split('/').pop()?.replace('.typ', '.png') ?? 'Test output',
vscode.ViewColumn.Beside,
- {}
)
+ newPanel.onDidChangeViewState(() => {
+ if (newPanel && newPanel.active && newPanel.visible) {
+ console.log(`Set sourceUriOfActivePanel to ${uri}`)
+ this.sourceUriOfActivePanel = uri
+ } else {
+ console.log(`Set sourceUriOfActivePanel to null`)
+ this.sourceUriOfActivePanel = null
+ }
+ })
+ newPanel.onDidDispose(() => {
+ console.log(`Delete panel ${uri}`)
+ this.panels.delete(uri)
+ if (this.sourceUriOfActivePanel === uri) {
+ this.sourceUriOfActivePanel = null
+ }
+ })
+ this.panels.set(uri, newPanel)
- refreshPanel("", "")
- })
-
- const refreshCmd = vscode.commands.registerCommand("ShortcutMenuBar.testRefresh", () => {
- refreshPanel("", "")
- })
+ this.refreshTestPreviewImpl_(uri, "", "")
+ }
- const rerunCmd = vscode.commands.registerCommand("ShortcutMenuBar.testRerun", () => {
- const uri = vscode.window.activeTextEditor.document.uri
+ /** @param {vscode.Uri} uri */
+ runTest(uri) {
const components = uri.fsPath.split(/tests[\/\\]/)
const dir = components[0]
const subPath = components[1]
+ this.testRunningStatusBarItem.show()
cp.exec(
`cargo test --manifest-path ${dir}/Cargo.toml --all --test tests -- ${subPath}`,
(err, stdout, stderr) => {
- console.log('Ran tests')
- refreshPanel(stdout, stderr)
+ this.testRunningStatusBarItem.hide()
+ console.log(`Ran tests ${uri.fsPath}`)
+ this.refreshTestPreviewImpl_(uri, stdout, stderr)
}
)
- })
+ }
+
+ /** @param {vscode.Uri} uri */
+ refreshTestPreview(uri) {
+ const panel = this.panels.get(uri)
+ if (panel) {
+ panel.reveal()
+ this.refreshTestPreviewImpl_(uri, "", "")
+ }
+ }
- const updateCmd = vscode.commands.registerCommand("ShortcutMenuBar.testUpdate", () => {
- const uri = vscode.window.activeTextEditor.document.uri
- const { pngPath, refPath } = getPaths(uri)
+ /** @param {vscode.Uri} uri */
+ updateTestReference(uri) {
+ const {png, ref} = TestHelper.getImageUris(uri)
- vscode.workspace.fs.copy(pngPath, refPath, { overwrite: true }).then(() => {
- console.log('Copied to reference file')
- cp.exec(`oxipng -o max -a ${refPath.fsPath}`, (err, stdout, stderr) => {
- refreshPanel(stdout, stderr)
+ vscode.workspace.fs.copy(png, ref, {overwrite: true})
+ .then(() => {
+ cp.exec(`oxipng -o max -a ${ref.fsPath}`, (err, stdout, stderr) => {
+ console.log(`Copied to reference file for ${uri.fsPath}`)
+ this.refreshTestPreviewImpl_(uri, stdout, stderr)
+ })
})
- })
- })
+ }
- context.subscriptions.push(openCmd)
- context.subscriptions.push(refreshCmd)
- context.subscriptions.push(rerunCmd)
- context.subscriptions.push(updateCmd)
+ /**
+ * @param {vscode.Uri} uri
+ * @param {string} webviewSection
+ */
+ copyFilePathToClipboard(uri, webviewSection) {
+ const {png, ref} = TestHelper.getImageUris(uri)
+ switch (webviewSection) {
+ case 'png':
+ vscode.env.clipboard.writeText(png.fsPath)
+ break
+ case 'ref':
+ vscode.env.clipboard.writeText(ref.fsPath)
+ break
+ default:
+ break
+ }
+ }
}
-function getPaths(uri) {
- const pngPath = vscode.Uri.file(uri.path
- .replace("tests/typ", "tests/png")
- .replace(".typ", ".png"))
-
- const refPath = vscode.Uri.file(uri.path
- .replace("tests/typ", "tests/ref")
- .replace(".typ", ".png"))
+/** @param {vscode.ExtensionContext} context */
+function activate(context) {
+ const manager = new TestHelper();
+ context.subscriptions.push(manager.testRunningStatusBarItem)
- return { pngPath, refPath }
+ context.subscriptions.push(vscode.commands.registerCommand(
+ "Typst.test-helper.openFromSource", () => {
+ manager.openTestPreview(TestHelper.getActiveDocumentUri())
+ }))
+ context.subscriptions.push(vscode.commands.registerCommand(
+ "Typst.test-helper.refreshFromSource", () => {
+ manager.refreshTestPreview(TestHelper.getActiveDocumentUri())
+ }))
+ context.subscriptions.push(vscode.commands.registerCommand(
+ "Typst.test-helper.refreshFromPreview", () => {
+ manager.refreshTestPreview(manager.getSourceUriOfActivePanel())
+ }))
+ context.subscriptions.push(vscode.commands.registerCommand(
+ "Typst.test-helper.runFromSource", () => {
+ manager.runTest(TestHelper.getActiveDocumentUri())
+ }))
+ context.subscriptions.push(vscode.commands.registerCommand(
+ "Typst.test-helper.runFromPreview", () => {
+ manager.runTest(manager.getSourceUriOfActivePanel())
+ }))
+ context.subscriptions.push(vscode.commands.registerCommand(
+ "Typst.test-helper.updateFromSource", () => {
+ manager.updateTestReference(TestHelper.getActiveDocumentUri())
+ }))
+ context.subscriptions.push(vscode.commands.registerCommand(
+ "Typst.test-helper.updateFromPreview", () => {
+ manager.updateTestReference(manager.getSourceUriOfActivePanel())
+ }))
+ // Context menu: the drop-down menu after right-click.
+ context.subscriptions.push(vscode.commands.registerCommand(
+ "Typst.test-helper.copyImageFilePathFromPreviewContext", (e) => {
+ manager.copyFilePathToClipboard(
+ manager.getSourceUriOfActivePanel(), e.webviewSection)
+ }))
}
-function getWebviewContent(pngSrc, refSrc, stdout, stderr) {
+/**
+ * @param {{png: vscode.Uri, ref: vscode.Uri}} webViewSrcs
+ * @param {string} stdout
+ * @param {string} stderr
+ * @returns {string}
+ */
+function getWebviewContent(webViewSrcs, stdout, stderr) {
+ const escape = (text) => text.replace(/</g, "&lt;").replace(/>/g, "&gt;")
return `
<!DOCTYPE html>
<html lang="en">
@@ -118,15 +256,15 @@ function getWebviewContent(pngSrc, refSrc, stdout, stderr) {
</style>
</head>
<body>
- <div class="flex">
+ <div class="flex" data-vscode-context='{"preventDefaultContextMenuItems": true}'>
<div>
<h1>Output</h1>
- <img src="${pngSrc}"/>
+ <img data-vscode-context='{"webviewSection":"png"}' src="${webViewSrcs.png}"/>
</div>
<div>
<h1>Reference</h1>
- <img src="${refSrc}"/>
+ <img data-vscode-context='{"webviewSection":"ref"}' src="${webViewSrcs.ref}"/>
</div>
</div>
@@ -140,10 +278,6 @@ function getWebviewContent(pngSrc, refSrc, stdout, stderr) {
`
}
-function escape(text) {
- return text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
-}
-
function deactivate() {}
-module.exports = { activate, deactivate }
+module.exports = {activate, deactivate}
diff --git a/tools/test-helper/package.json b/tools/test-helper/package.json
index d2578ab9..de0c9456 100644
--- a/tools/test-helper/package.json
+++ b/tools/test-helper/package.json
@@ -1,82 +1,118 @@
{
- "name": "typst-test-helper",
+ "name": "test-helper",
+ "publisher": "typst",
"displayName": "Typst Test Helper",
"description": "Helps to run, compare and update Typst tests.",
"version": "0.0.1",
"engines": {
- "vscode": "^1.53.0"
+ "vscode": "^1.71.0"
},
"categories": [
"Other"
],
"activationEvents": [
- "onCommand:ShortcutMenuBar.testOpen",
- "onCommand:ShortcutMenuBar.testRefresh",
- "onCommand:ShortcutMenuBar.testRerun",
- "onCommand:ShortcutMenuBar.testUpdate"
+ "onCommand:Typst.test-helper.openFromSource",
+ "onCommand:Typst.test-helper.refreshFromSource",
+ "onCommand:Typst.test-helper.refreshFromPreview",
+ "onCommand:Typst.test-helper.runFromSource",
+ "onCommand:Typst.test-helper.runFromPreview",
+ "onCommand:Typst.test-helper.updateFromSource",
+ "onCommand:Typst.test-helper.updateFromPreview",
+ "onCommand:Typst.test-helper.copyImageFilePathFromPreviewContext"
],
"main": "./extension.js",
"contributes": {
"commands": [
{
- "command": "ShortcutMenuBar.testOpen",
+ "command": "Typst.test-helper.openFromSource",
"title": "Open test output",
- "category": "ShortcutMenuBar",
- "icon": {
- "light": "images/open-light.svg",
- "dark": "images/open-dark.svg"
- }
+ "category": "Typst.test-helper",
+ "icon": "$(plus)"
},
{
- "command": "ShortcutMenuBar.testRefresh",
+ "command": "Typst.test-helper.refreshFromSource",
"title": "Refresh preview",
- "category": "ShortcutMenuBar",
- "icon": {
- "light": "images/refresh-light.svg",
- "dark": "images/refresh-dark.svg"
- }
+ "category": "Typst.test-helper",
+ "icon": "$(refresh)"
},
{
- "command": "ShortcutMenuBar.testRerun",
- "title": "Rerun test",
- "category": "ShortcutMenuBar",
- "icon": {
- "light": "images/rerun-light.svg",
- "dark": "images/rerun-dark.svg"
- }
+ "command": "Typst.test-helper.refreshFromPreview",
+ "title": "Refresh preview",
+ "category": "Typst.test-helper",
+ "icon": "$(refresh)"
+ },
+ {
+ "command": "Typst.test-helper.runFromSource",
+ "title": "Run test",
+ "category": "Typst.test-helper",
+ "icon": "$(debug-start)"
+ },
+ {
+ "command": "Typst.test-helper.runFromPreview",
+ "title": "Run test",
+ "category": "Typst.test-helper",
+ "icon": "$(debug-start)"
},
{
- "command": "ShortcutMenuBar.testUpdate",
+ "command": "Typst.test-helper.updateFromSource",
"title": "Update reference image",
- "category": "ShortcutMenuBar",
- "icon": {
- "light": "images/update-light.svg",
- "dark": "images/update-dark.svg"
- }
+ "category": "Typst.test-helper",
+ "icon": "$(save)"
+ },
+ {
+ "command": "Typst.test-helper.updateFromPreview",
+ "title": "Update reference image",
+ "category": "Typst.test-helper",
+ "icon": "$(save)"
+ },
+ {
+ "command": "Typst.test-helper.copyImageFilePathFromPreviewContext",
+ "title": "Copy image file path"
}
],
"menus": {
"editor/title": [
{
"when": "resourceExtname == .typ && resourcePath =~ /.*tests.*/",
- "command": "ShortcutMenuBar.testOpen",
- "group": "navigation@0"
+ "command": "Typst.test-helper.openFromSource",
+ "group": "navigation@1"
},
{
"when": "resourceExtname == .typ && resourcePath =~ /.*tests.*/",
- "command": "ShortcutMenuBar.testRefresh",
- "group": "navigation@1"
+ "command": "Typst.test-helper.refreshFromSource",
+ "group": "navigation@2"
},
{
"when": "resourceExtname == .typ && resourcePath =~ /.*tests.*/",
- "command": "ShortcutMenuBar.testRerun",
- "group": "navigation@2"
+ "command": "Typst.test-helper.runFromSource",
+ "group": "navigation@3"
},
{
"when": "resourceExtname == .typ && resourcePath =~ /.*tests.*/",
- "command": "ShortcutMenuBar.testUpdate",
+ "command": "Typst.test-helper.updateFromSource",
+ "group": "navigation@4"
+ },
+ {
+ "when": "activeWebviewPanelId == Typst.test-helper.preview",
+ "command": "Typst.test-helper.refreshFromPreview",
+ "group": "navigation@1"
+ },
+ {
+ "when": "activeWebviewPanelId == Typst.test-helper.preview",
+ "command": "Typst.test-helper.runFromPreview",
+ "group": "navigation@2"
+ },
+ {
+ "when": "activeWebviewPanelId == Typst.test-helper.preview",
+ "command": "Typst.test-helper.updateFromPreview",
"group": "navigation@3"
}
+ ],
+ "webview/context": [
+ {
+ "command": "Typst.test-helper.copyImageFilePathFromPreviewContext",
+ "when": "webviewId == Typst.test-helper.preview && (webviewSection == png || webviewSection == ref)"
+ }
]
}
},
@@ -84,4 +120,4 @@
"@types/vscode": "^1.53.0",
"@types/node": "^12.11.7"
}
-}
+} \ No newline at end of file