Skip to content

Commit e737281

Browse files
committed
Move Workbench commands to support file
1 parent 9ba6a44 commit e737281

File tree

3 files changed

+55
-413
lines changed

3 files changed

+55
-413
lines changed

test/e2e/support/commands.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import "cypress-wait-until";
55
import { parse, stringify } from "smol-toml";
66
import "./selectors";
77
import "./sequences";
8+
import "./workbench";
89

910
const connectManagerServer = Cypress.env("CONNECT_MANAGER_URL");
1011

test/e2e/tests/workbench/deployments.cy.js renamed to test/e2e/support/workbench.js

Lines changed: 51 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,22 @@
11
// Copyright (C) 2025 by Posit Software, PBC.
22

3-
const WORKBENCH_BASE_URL = Cypress.env("WORKBENCH_URL");
4-
5-
describe("Workbench > Positron", { baseUrl: WORKBENCH_BASE_URL }, () => {
6-
// Each test must set this var to enable project-specific cleanup
7-
let projectDir;
8-
9-
beforeEach(() => {
10-
cy.cleanupAndRestartWorkbench();
11-
12-
cy.resetConnect();
13-
cy.setAdminCredentials();
14-
cy.visit("/");
15-
16-
cy.loginToWorkbench();
17-
});
18-
19-
afterEach(() => {
20-
cy.cleanupWorkbenchData(projectDir);
21-
});
22-
23-
it("Static Content Deployment", () => {
24-
projectDir = "static";
25-
26-
cy.startWorkbenchPositronPythonProject(projectDir);
27-
28-
// Publish the content
29-
cy.createPositronDeployment(
30-
projectDir,
31-
"index.html",
32-
"static",
33-
(tomlFiles) => {
34-
const config = tomlFiles.config.contents;
35-
expect(config.title).to.equal(projectDir);
36-
expect(config.type).to.equal("html");
37-
expect(config.entrypoint).to.equal("index.html");
38-
expect(config.files[0]).to.equal("/index.html");
39-
expect(config.files[1]).to.equal(
40-
`/.posit/publish/${tomlFiles.config.name}`,
41-
);
42-
expect(config.files[2]).to.equal(
43-
`/.posit/publish/deployments/${tomlFiles.contentRecord.name}`,
44-
);
45-
},
46-
).deployCurrentlySelected();
47-
});
48-
});
3+
/**
4+
* Support file containing Workbench-specific Cypress commands
5+
*/
496

507
/**
51-
* Logs into Workbench using default credentials and waits for the UI to load.
8+
* Logs into Workbench using default credentials and waits for the UI to load
9+
* Uses the default Workbench username/password as documented here: https://hub.docker.com/r/rstudio/rstudio-workbench
5210
* @param {string} username - The username to login with (defaults to "rstudio")
5311
* @param {string} password - The password to login with (defaults to "rstudio")
5412
*/
5513
Cypress.Commands.add(
56-
"loginToWorkbench",
14+
"visitAndLoginToWorkbench",
5715
(username = "rstudio", password = "rstudio") => {
5816
cy.log(`Logging into Workbench as ${username}`);
5917

18+
cy.visit("/");
19+
6020
// Enter credentials and submit the form
6121
cy.get("#username").type(username);
6222
cy.get("#password").type(password);
@@ -102,16 +62,23 @@ Cypress.Commands.add("cleanupWorkbenchData", (projectDir) => {
10262

10363
// Remove workbench e2e projects
10464
cy.exec(
105-
'for dir in content-workspace/workbench-e2e-*; do rm -rf "$dir" 2>/dev/null || true; done',
65+
`for dir in content-workspace/${Cypress.env("WORKBENCH_PROJECT_PREFIX")}*; do rm -rf "$dir" 2>/dev/null || true; done`,
10666
{
10767
failOnNonZeroExit: false,
10868
},
10969
).then((result) => {
110-
cy.log(`Bash loop cleanup result: code ${result.code}`);
111-
if (result.stderr) cy.log(`Cleanup stderr: ${result.stderr}`);
70+
cy.log(`Cleanup projects result: code ${result.code}`);
71+
if (result.stderr) cy.log(`Cleanup projects stderr: ${result.stderr}`);
11272
});
11373
});
11474

75+
/**
76+
* Cleans up and restarts the Workbench container to ensure a fresh state
77+
* This function stops the current container, removes any test data, and starts a fresh container
78+
* with clean state. It handles container lifecycle operations using the justfile commands
79+
*
80+
* @param {string} projectDir - Optional project directory to clean up specific project data
81+
*/
11582
Cypress.Commands.add("cleanupAndRestartWorkbench", (projectDir) => {
11683
// Stop and remove the container
11784
cy.log("Stopping and removing Workbench container");
@@ -199,7 +166,7 @@ Cypress.Commands.add("startWorkbenchPositronPythonProject", () => {
199166

200167
// Set a randomized project name
201168
cy.contains("Set project name").should("be.visible");
202-
const projectName = `workbench-e2e-${Math.random().toString(36).slice(2, 10)}`;
169+
const projectName = `${Cypress.env("WORKBENCH_PROJECT_PREFIX")}${Math.random().toString(36).slice(2, 10)}`;
203170
cy.contains("span", "Enter a name for your new Python Project")
204171
.parent("label")
205172
.find("input")
@@ -241,34 +208,42 @@ Cypress.Commands.add("startWorkbenchPositronPythonProject", () => {
241208
timeout: 30_000,
242209
}).should("be.visible");
243210

244-
// TODO need some other command to wait for more stuff in the IDE before proceeding
245-
// Observed a session failed to start and prevented the rest of the test from working
246-
247211
cy.log(`Successfully created Python project: ${projectName}`);
248212
});
249213

214+
/**
215+
* Creates a new Positron deployment in Workbench
216+
*
217+
* Note: This function is derived from the VS Code deployment flow in the main Publisher extension tests,
218+
* but has been specifically adapted for the Workbench UI environment which has different navigation
219+
* patterns, element selectors, and timing considerations. In particular, the Workbench integration
220+
* requires additional steps to handle the Positron editor context and different menu structures.
221+
*
222+
* @param {string} projectDir - The directory containing the project to deploy
223+
* @param {string} entrypointFile - The primary file to use as the entrypoint (e.g., "index.html")
224+
* @param {string} title - The title to give to the deployment
225+
* @param {Function} verifyTomlCallback - Callback function to verify TOML configuration
226+
* The callback receives an object with structure: {
227+
* config: { name: string, path: string, contents: Object },
228+
* contentRecord: { name: string, path: string, contents: Object }
229+
* }
230+
* @returns {Cypress.Chainable} - Chain that can be extended with additional deployment actions
231+
*/
250232
Cypress.Commands.add(
251233
"createPositronDeployment",
252-
(
253-
projectDir, // string
254-
entrypointFile, // string
255-
title, // string
256-
verifyTomlCallback, // func({config: { filename: string, contents: {},}, contentRecord: { filename: string, contents: {}})
257-
) => {
234+
(projectDir, entrypointFile, title, verifyTomlCallback) => {
258235
// Temporarily ignore uncaught exception due to a vscode worker being cancelled at some point
259236
cy.on("uncaught:exception", () => false);
260237

261238
// Open the entrypoint ahead of time for easier selection later
262-
// expand the subdirectory
263-
if (projectDir !== ".") {
264-
// Open the folder with our content
265-
cy.get("button").contains("Open Folder...").click();
266-
cy.get(".quick-input-widget").within(() => {
267-
cy.get(".quick-input-box input").should("be.visible");
268-
cy.get('.monaco-list-row[aria-label="static"]').click();
269-
cy.get(".quick-input-header a[role='button']").contains("OK").click();
270-
});
271-
}
239+
// Uses the Workbench-specific file open flow
240+
// Note this deviates from the VS Code logic and does not currently handle projectDir = "."
241+
cy.get("button").contains("Open Folder...").click();
242+
cy.get(".quick-input-widget").within(() => {
243+
cy.get(".quick-input-box input").should("be.visible");
244+
cy.get('.monaco-list-row[aria-label="static"]').click();
245+
cy.get(".quick-input-header a[role='button']").contains("OK").click();
246+
});
272247

273248
// open the entrypoint file
274249
cy.get(".explorer-viewlet")
@@ -281,19 +256,19 @@ Cypress.Commands.add(
281256
.find(`[aria-label="${entrypointFile}"]`)
282257
.should("be.visible");
283258

284-
// activate the publisher extension
285-
// Open Publisher, due to viewport size it is buried in the "..." menu
259+
// Activate the publisher extension
260+
// In Workbench the viewport size causes Publisher to be in the "..." menu
286261
cy.get(
287262
'[id="workbench.parts.activitybar"] .action-item[role="button"][aria-label="Additional Views"]',
288263
{
289264
timeout: 30_000,
290265
},
291266
).click();
292-
// Wait for popup menu to appear
267+
// Wait for popup menu that contains Publisher to appear
293268
cy.get('.monaco-menu .actions-container[role="menu"]')
294269
.should("be.visible")
295270
.within(() => {
296-
// Finally, double-click the Posit Publisher menu item
271+
// Finally, double-click the Posit Publisher menu item, single click often fails to open it
297272
cy.findByLabelText("Posit Publisher").dblclick();
298273
});
299274

@@ -318,7 +293,7 @@ Cypress.Commands.add(
318293
// Create a new deployment
319294
cy.get(".quick-input-list").contains("Create a New Deployment").click();
320295

321-
// prompt for select entrypoint
296+
// Prompt for select entrypoint
322297
// Note this deviates from the VS Code logic and does not currently handle projectDir = "."
323298
const targetLabel = `${entrypointFile}, Open Files`;
324299
cy.get(".quick-input-widget")

0 commit comments

Comments
 (0)