From f613b53c52bc79439843e5a638c82b0fcb355758 Mon Sep 17 00:00:00 2001 From: zackverham <96081108+zackverham@users.noreply.github.com> Date: Mon, 9 Mar 2026 15:40:02 -0400 Subject: [PATCH 1/5] Add 6 high-priority E2E tests for redeployment, Quarto, multi-deployment switching, file toggling, packages, and deployment status Addresses coverage gaps identified in E2E test analysis: - redeployment.cy.js: deploy, modify content, redeploy; verify same record reused - quarto-deployment.cy.js: Quarto document deployment with new fixture - multi-deployment-switching.cy.js: create two deployments, switch via picker - file-include-exclude.cy.js: toggle file checkboxes, verify TOML updates - package-management.cy.js: verify Python packages section shows detected packages - deployment-logs.cy.js: verify post-deploy status, View Content button, record fields Also adds modifyFileInContainer and countDeploymentRecordFiles custom commands and a quarto-doc content fixture (markdown-only, no Python/R engine). Co-Authored-By: Claude Opus 4.6 --- .../content-workspace/quarto-doc/_quarto.yml | 2 + .../quarto-doc/quarto-doc.qmd | 16 ++ test/e2e/support/commands.js | 32 ++++ test/e2e/tests/deployment-logs.cy.js | 72 +++++++++ test/e2e/tests/file-include-exclude.cy.js | 108 +++++++++++++ .../tests/multi-deployment-switching.cy.js | 146 ++++++++++++++++++ test/e2e/tests/package-management.cy.js | 53 +++++++ test/e2e/tests/quarto-deployment.cy.js | 56 +++++++ test/e2e/tests/redeployment.cy.js | 107 +++++++++++++ 9 files changed, 592 insertions(+) create mode 100644 test/e2e/content-workspace/quarto-doc/_quarto.yml create mode 100644 test/e2e/content-workspace/quarto-doc/quarto-doc.qmd create mode 100644 test/e2e/tests/deployment-logs.cy.js create mode 100644 test/e2e/tests/file-include-exclude.cy.js create mode 100644 test/e2e/tests/multi-deployment-switching.cy.js create mode 100644 test/e2e/tests/package-management.cy.js create mode 100644 test/e2e/tests/quarto-deployment.cy.js create mode 100644 test/e2e/tests/redeployment.cy.js diff --git a/test/e2e/content-workspace/quarto-doc/_quarto.yml b/test/e2e/content-workspace/quarto-doc/_quarto.yml new file mode 100644 index 000000000..cdaf6c94d --- /dev/null +++ b/test/e2e/content-workspace/quarto-doc/_quarto.yml @@ -0,0 +1,2 @@ +project: + title: "quarto-doc" diff --git a/test/e2e/content-workspace/quarto-doc/quarto-doc.qmd b/test/e2e/content-workspace/quarto-doc/quarto-doc.qmd new file mode 100644 index 000000000..dc6c63941 --- /dev/null +++ b/test/e2e/content-workspace/quarto-doc/quarto-doc.qmd @@ -0,0 +1,16 @@ +--- +title: "Publisher E2E Test - Quarto Document" +format: html +--- + +## Quarto Document + +This is a simple Quarto document used for E2E testing of Posit Publisher. + +The document uses **markdown only** (no Python or R engine) to keep dependencies minimal. + +### Sample Content + +- Item one +- Item two +- Item three diff --git a/test/e2e/support/commands.js b/test/e2e/support/commands.js index 11b77fb76..82e1ef7c7 100644 --- a/test/e2e/support/commands.js +++ b/test/e2e/support/commands.js @@ -849,5 +849,37 @@ Cypress.Commands.add("deletePCCContent", () => { }); }); +// modifyFileInContainer +// Purpose: Write file content inside the Docker container via `docker exec`. +// When to use: Tests that need to modify workspace files (e.g., redeployment after content change). +Cypress.Commands.add("modifyFileInContainer", (containerPath, content) => { + const escaped = content.replace(/\\/g, "\\\\").replace(/'/g, "'\"'\"'"); + return cy + .exec( + `docker exec publisher-e2e.code-server bash -c "cat <<'MODEOF' > '${containerPath}'\n${escaped}\nMODEOF"`, + ) + .then((result) => { + if (result.exitCode !== 0) { + throw new Error( + `Failed to modify file in container: ${result.stderr}`, + ); + } + }); +}); + +// countDeploymentRecordFiles +// Purpose: Count *.toml files in .posit/publish/deployments/ for a project directory. +// When to use: Verifying redeployment reuses the same record (count stays at 1). +Cypress.Commands.add("countDeploymentRecordFiles", (projectDir) => { + const targetDir = `content-workspace/${projectDir}/.posit/publish/deployments`; + return cy + .exec(`ls -1 ${targetDir}/*.toml 2>/dev/null | wc -l`, { + failOnNonZeroExit: false, + }) + .then((result) => { + return parseInt(result.stdout.trim(), 10) || 0; + }); +}); + // NOTE: Specific exception handling is done in support/index.js // Do not add a catch-all here as it masks real errors. diff --git a/test/e2e/tests/deployment-logs.cy.js b/test/e2e/tests/deployment-logs.cy.js new file mode 100644 index 000000000..cf4552d3b --- /dev/null +++ b/test/e2e/tests/deployment-logs.cy.js @@ -0,0 +1,72 @@ +// Copyright (C) 2025 by Posit Software, PBC. + +// Purpose: Verify deployment status and post-deployment UI elements. +// - Deploys static content. +// - Asserts deploy-status shows "Last Deployment Successful". +// - Verifies "View Content" button is visible. +// - Validates deployment record contains expected fields (dashboard_url, direct_url, deployed_at, bundle_id). +describe("Deployment Status Section", () => { + const projectDir = "static"; + + before(() => { + cy.initializeConnect(); + }); + + beforeEach(() => { + cy.visit("/"); + cy.getPublisherSidebarIcon().click(); + cy.waitForPublisherIframe(); + cy.debugIframes(); + }); + + afterEach(() => { + cy.clearupDeployments(projectDir); + }); + + it("Shows deployment status and View Content button after successful deploy", () => { + cy.expectInitialPublisherState(); + + // Deploy static content + cy.createPCSDeployment( + projectDir, + "index.html", + "static-status-test", + (tomlFiles) => { + const { contents: config } = tomlFiles.config; + expect(config.title).to.equal("static-status-test"); + expect(config.type).to.equal("html"); + return tomlFiles; + }, + ).deployCurrentlySelected(); + + // Verify deploy-status shows success + cy.findInPublisherWebview('[data-automation="deploy-status"]').should( + "contain.text", + "Last Deployment Successful", + ); + + // Verify "View Content" button is visible + cy.publisherWebview() + .findByText("View Content") + .should("be.visible"); + + // Verify deployment record has expected fields + cy.getPublisherTomlFilePaths(projectDir).then((filePaths) => { + cy.loadTomlFile(filePaths.contentRecord.path).then((contentRecord) => { + expect(contentRecord.dashboard_url).to.exist; + expect(contentRecord.dashboard_url).to.be.a("string"); + expect(contentRecord.dashboard_url).to.not.be.empty; + + expect(contentRecord.direct_url).to.exist; + expect(contentRecord.direct_url).to.be.a("string"); + expect(contentRecord.direct_url).to.not.be.empty; + + expect(contentRecord.deployed_at).to.exist; + expect(contentRecord.deployed_at).to.be.a("string"); + expect(contentRecord.deployed_at).to.not.be.empty; + + expect(contentRecord.bundle_id).to.exist; + }); + }); + }); +}); diff --git a/test/e2e/tests/file-include-exclude.cy.js b/test/e2e/tests/file-include-exclude.cy.js new file mode 100644 index 000000000..cb939c6fa --- /dev/null +++ b/test/e2e/tests/file-include-exclude.cy.js @@ -0,0 +1,108 @@ +// Copyright (C) 2025 by Posit Software, PBC. + +// Purpose: Verify file include/exclude toggling in the project files tree. +// - Creates a PCS deployment for fastapi-simple. +// - Unchecks requirements.txt in the project files tree. +// - Verifies the TOML config no longer includes /requirements.txt. +// - Re-checks requirements.txt. +// - Verifies the TOML config includes /requirements.txt again. +describe("File Include/Exclude Section", () => { + const projectDir = "fastapi-simple"; + + before(() => { + cy.initializeConnect(); + }); + + beforeEach(() => { + cy.visit("/"); + cy.getPublisherSidebarIcon().click(); + cy.waitForPublisherIframe(); + cy.debugIframes(); + }); + + afterEach(() => { + cy.clearupDeployments(projectDir); + }); + + it("Toggle file inclusion via checkbox updates TOML config", () => { + cy.skipIfConnectVersionBefore("2025.03"); + + cy.expectInitialPublisherState(); + + cy.createPCSDeployment( + projectDir, + "fastapi-main.py", + "fastapi-file-toggle", + (tomlFiles) => { + const { contents: config } = tomlFiles.config; + expect(config.title).to.equal("fastapi-file-toggle"); + expect(config.files).to.include("/requirements.txt"); + return tomlFiles; + }, + ); + + // Step 1: Find requirements.txt in the project files tree and uncheck it + cy.retryWithBackoff( + () => + cy + .publisherWebview() + .find('[data-automation="project-files"]') + .contains(".tree-item-title", "requirements.txt") + .closest(".tree-item") + .find('.vscode-checkbox input[type="checkbox"]'), + 10, + 1000, + ) + .should("exist") + .should("be.visible") + .then(($checkbox) => { + // Should be checked initially + expect($checkbox.prop("checked")).to.be.true; + cy.wrap($checkbox).click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.wrap($checkbox).should("not.be.checked"); + }); + + // Step 2: Verify TOML no longer includes requirements.txt + // Wait for the async TOML update to propagate + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(1000); + cy.getPublisherTomlFilePaths(projectDir).then((filePaths) => { + cy.loadTomlFile(filePaths.config.path).then((config) => { + expect(config.files).to.not.include("/requirements.txt"); + }); + }); + + // Step 3: Re-check requirements.txt + cy.retryWithBackoff( + () => + cy + .publisherWebview() + .find('[data-automation="project-files"]') + .contains(".tree-item-title", "requirements.txt") + .closest(".tree-item") + .find('.vscode-checkbox input[type="checkbox"]'), + 10, + 1000, + ) + .should("exist") + .should("be.visible") + .then(($checkbox) => { + expect($checkbox.prop("checked")).to.be.false; + cy.wrap($checkbox).click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.wrap($checkbox).should("be.checked"); + }); + + // Step 4: Verify TOML includes requirements.txt again + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(1000); + cy.getPublisherTomlFilePaths(projectDir).then((filePaths) => { + cy.loadTomlFile(filePaths.config.path).then((config) => { + expect(config.files).to.include("/requirements.txt"); + }); + }); + }); +}); diff --git a/test/e2e/tests/multi-deployment-switching.cy.js b/test/e2e/tests/multi-deployment-switching.cy.js new file mode 100644 index 000000000..30797c73a --- /dev/null +++ b/test/e2e/tests/multi-deployment-switching.cy.js @@ -0,0 +1,146 @@ +// Copyright (C) 2025 by Posit Software, PBC. + +// Purpose: Verify switching between multiple deployments. +// - Creates a deployment for static content. +// - Creates a second deployment for fastapi-simple. +// - Switches back to the first deployment via the deployment picker. +// - Asserts the entrypoint-label text changes to match the selected deployment. +describe("Multi-Deployment Switching Section", () => { + before(() => { + cy.initializeConnect(); + }); + + beforeEach(() => { + cy.visit("/"); + cy.getPublisherSidebarIcon().click(); + cy.waitForPublisherIframe(); + cy.debugIframes(); + }); + + afterEach(() => { + cy.clearupDeployments("static"); + cy.clearupDeployments("fastapi-simple"); + }); + + it("Switch between two deployments via picker", () => { + cy.skipIfConnectVersionBefore("2025.03"); + + cy.expectInitialPublisherState(); + + // Step 1: Create first deployment (static) + cy.createPCSDeployment( + "static", + "index.html", + "static-multi-test", + (tomlFiles) => { + const { contents: config } = tomlFiles.config; + expect(config.title).to.equal("static-multi-test"); + expect(config.type).to.equal("html"); + return tomlFiles; + }, + ); + + // Step 2: Verify first deployment is shown in entrypoint-label + cy.findInPublisherWebview('[data-automation="entrypoint-label"]').should( + "contain.text", + "static-multi-test", + ); + + // Step 3: Create second deployment (fastapi-simple) via the deployment picker + cy.publisherWebview() + .find(".deployment-control") + .first() + .then((control) => { + Cypress.$(control).trigger("click"); + }); + + cy.get(".quick-input-widget").should("be.visible"); + cy.get(".quick-input-titlebar").should("have.text", "Select Deployment"); + cy.get(".quick-input-list") + .find('[aria-label*="Create a New Deployment"]') + .should("be.visible") + .click(); + + // Select entrypoint for second deployment + cy.retryWithBackoff( + () => + cy + .get(".quick-input-widget") + .find( + '[aria-label="fastapi-simple/fastapi-main.py, Open Files"], [aria-label="fastapi-main.py, Open Files"]', + ), + 10, + 700, + ).then(($el) => { + cy.wrap($el).scrollIntoView(); + cy.wrap($el).click({ force: true }); + }); + + // Wait for title step and enter title + cy.retryWithBackoff( + () => + cy + .get(".quick-input-widget") + .find(".quick-input-message") + .then(($m) => { + const txt = ($m.text() || "").toLowerCase(); + return /title|name/.test(txt) ? $m : Cypress.$(); + }), + 10, + 700, + ); + + cy.get(".quick-input-widget") + .find(".quick-input-filter input") + .then(($input) => { + const el = $input[0]; + el.value = ""; + el.dispatchEvent(new Event("input", { bubbles: true })); + el.value = "fastapi-multi-test"; + el.dispatchEvent(new Event("input", { bubbles: true })); + el.dispatchEvent(new Event("change", { bubbles: true })); + }); + + cy.get(".quick-input-widget") + .find(".quick-input-filter input") + .should("have.value", "fastapi-multi-test"); + + cy.get(".quick-input-widget").type("{enter}"); + + // Select credential + cy.get( + '.quick-input-widget .monaco-list-row[aria-label*="admin-code-server"]', + ) + .first() + .click({ force: true }); + + // Step 4: Verify second deployment is now selected + cy.findInPublisherWebview('[data-automation="entrypoint-label"]').should( + "contain.text", + "fastapi-multi-test", + ); + + // Step 5: Switch back to first deployment via picker + cy.publisherWebview() + .find(".deployment-control") + .first() + .then((control) => { + Cypress.$(control).trigger("click"); + }); + + cy.get(".quick-input-widget").should("be.visible"); + cy.get(".quick-input-titlebar").should("have.text", "Select Deployment"); + + // Select the first deployment (static-multi-test) + cy.get(".quick-input-list") + .find('[aria-label*="static-multi-test"]') + .should("be.visible") + .click(); + + // Step 6: Verify first deployment is shown again + cy.findInPublisherWebview('[data-automation="entrypoint-label"]').should( + "contain.text", + "static-multi-test", + ); + }); +}); diff --git a/test/e2e/tests/package-management.cy.js b/test/e2e/tests/package-management.cy.js new file mode 100644 index 000000000..8b6befcc4 --- /dev/null +++ b/test/e2e/tests/package-management.cy.js @@ -0,0 +1,53 @@ +// Copyright (C) 2025 by Posit Software, PBC. + +// Purpose: Verify Python packages section displays detected packages. +// - Creates a PCS deployment for fastapi-simple. +// - Asserts the python-packages section exists in the sidebar. +// - Verifies "fastapi" appears in the package list (from requirements.txt). +describe("Package Management Section", () => { + const projectDir = "fastapi-simple"; + + before(() => { + cy.initializeConnect(); + }); + + beforeEach(() => { + cy.visit("/"); + cy.getPublisherSidebarIcon().click(); + cy.waitForPublisherIframe(); + cy.debugIframes(); + }); + + afterEach(() => { + cy.clearupDeployments(projectDir); + }); + + it("Python packages section shows detected packages", () => { + cy.skipIfConnectVersionBefore("2025.03"); + + cy.expectInitialPublisherState(); + + cy.createPCSDeployment( + projectDir, + "fastapi-main.py", + "fastapi-packages-test", + (tomlFiles) => { + const { contents: config } = tomlFiles.config; + expect(config.title).to.equal("fastapi-packages-test"); + expect(config.type).to.equal("python-fastapi"); + return tomlFiles; + }, + ); + + // Verify the Python Packages section exists + cy.findInPublisherWebview('[data-automation="python-packages"]').should( + "exist", + ); + + // Verify fastapi appears in the package list + cy.findInPublisherWebview('[data-automation="python-packages"]').should( + "contain.text", + "fastapi", + ); + }); +}); diff --git a/test/e2e/tests/quarto-deployment.cy.js b/test/e2e/tests/quarto-deployment.cy.js new file mode 100644 index 000000000..c9e9ebdd3 --- /dev/null +++ b/test/e2e/tests/quarto-deployment.cy.js @@ -0,0 +1,56 @@ +// Copyright (C) 2025 by Posit Software, PBC. + +// Purpose: Verify Quarto document deployment via PCS. +// - Creates a deployment for a markdown-only Quarto doc (no Python/R engine). +// - Validates TOML config: type, entrypoint, files include _quarto.yml. +// - Deploys and verifies success. +describe("Quarto Deployment Section", () => { + const projectDir = "quarto-doc"; + + before(() => { + cy.initializeConnect(); + }); + + beforeEach(() => { + cy.visit("/"); + cy.getPublisherSidebarIcon().click(); + cy.waitForPublisherIframe(); + cy.debugIframes(); + }); + + afterEach(() => { + cy.clearupDeployments(projectDir); + }); + + it("PCS Quarto Document Deployment", () => { + cy.expectInitialPublisherState(); + + cy.createPCSDeployment( + projectDir, + "quarto-doc.qmd", + "quarto-doc-test", + (tomlFiles) => { + const { contents: config } = tomlFiles.config; + const { name: cfgName } = tomlFiles.config; + const { name: recName } = tomlFiles.contentRecord; + + expect(config.title).to.equal("quarto-doc-test"); + expect(config.type).to.equal("quarto-static"); + expect(config.entrypoint).to.equal("quarto-doc.qmd"); + + // Assert required files without relying on order + expect(config.files).to.include.members([ + "/quarto-doc.qmd", + "/_quarto.yml", + `/.posit/publish/${cfgName}`, + `/.posit/publish/deployments/${recName}`, + ]); + return tomlFiles; + }, + ).deployCurrentlySelected(); + + cy.findUniqueInPublisherWebview( + '[data-automation="publisher-deployment-section"]', + ).should("exist"); + }); +}); diff --git a/test/e2e/tests/redeployment.cy.js b/test/e2e/tests/redeployment.cy.js new file mode 100644 index 000000000..690dc27ee --- /dev/null +++ b/test/e2e/tests/redeployment.cy.js @@ -0,0 +1,107 @@ +// Copyright (C) 2025 by Posit Software, PBC. + +// Purpose: Verify redeployment workflow - deploy, modify content, redeploy. +// - Deploys static content, captures initial bundle_id. +// - Modifies index.html inside the container. +// - Redeploys and verifies: same record file (no new record created), updated bundle_id. +describe("Redeployment Section", () => { + const projectDir = "static"; + const originalHtml = ` + + + + + Publisher e2e Test - Static Content + + +

Publisher e2e Test - Static Content

+ + +`; + + before(() => { + cy.initializeConnect(); + }); + + beforeEach(() => { + cy.visit("/"); + cy.getPublisherSidebarIcon().click(); + cy.waitForPublisherIframe(); + cy.debugIframes(); + }); + + afterEach(() => { + // Restore original index.html inside the container + cy.modifyFileInContainer( + "/home/coder/workspace/static/index.html", + originalHtml, + ); + cy.clearupDeployments(projectDir); + }); + + it("Redeploy after content modification uses same deployment record", () => { + cy.expectInitialPublisherState(); + + // Step 1: Create deployment and deploy + cy.createPCSDeployment( + projectDir, + "index.html", + "static-redeploy-test", + (tomlFiles) => { + const { contents: config } = tomlFiles.config; + expect(config.title).to.equal("static-redeploy-test"); + expect(config.type).to.equal("html"); + expect(config.entrypoint).to.equal("index.html"); + return tomlFiles; + }, + ).deployCurrentlySelected(); + + // Step 2: Capture initial deployment record state + let initialBundleId; + cy.getPublisherTomlFilePaths(projectDir).then((filePaths) => { + cy.loadTomlFile(filePaths.contentRecord.path).then((contentRecord) => { + initialBundleId = contentRecord.bundle_id; + expect(initialBundleId).to.exist; + }); + }); + + // Verify exactly 1 deployment record file + cy.countDeploymentRecordFiles(projectDir).then((count) => { + expect(count).to.equal(1); + }); + + // Step 3: Modify content inside the container + const modifiedHtml = ` + + + + + Publisher e2e Test - Static Content (Modified) + + +

Publisher e2e Test - Static Content (Modified)

+ + +`; + cy.modifyFileInContainer( + "/home/coder/workspace/static/index.html", + modifiedHtml, + ); + + // Step 4: Redeploy + cy.deployCurrentlySelected(); + + // Step 5: Verify same record file (still exactly 1) + cy.countDeploymentRecordFiles(projectDir).then((count) => { + expect(count).to.equal(1); + }); + + // Step 6: Verify bundle_id changed (new bundle uploaded) + cy.getPublisherTomlFilePaths(projectDir).then((filePaths) => { + cy.loadTomlFile(filePaths.contentRecord.path).then((contentRecord) => { + expect(contentRecord.bundle_id).to.exist; + expect(contentRecord.bundle_id).to.not.equal(initialBundleId); + }); + }); + }); +}); From 11ed2547abc7588044c85d9a76955005fc9a5003 Mon Sep 17 00:00:00 2001 From: zackverham <96081108+zackverham@users.noreply.github.com> Date: Mon, 9 Mar 2026 15:45:23 -0400 Subject: [PATCH 2/5] Update copyright headers to 2026 in new test files Co-Authored-By: Claude Opus 4.6 --- test/e2e/tests/deployment-logs.cy.js | 2 +- test/e2e/tests/file-include-exclude.cy.js | 2 +- test/e2e/tests/multi-deployment-switching.cy.js | 2 +- test/e2e/tests/package-management.cy.js | 2 +- test/e2e/tests/quarto-deployment.cy.js | 2 +- test/e2e/tests/redeployment.cy.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/e2e/tests/deployment-logs.cy.js b/test/e2e/tests/deployment-logs.cy.js index cf4552d3b..c52cdb10d 100644 --- a/test/e2e/tests/deployment-logs.cy.js +++ b/test/e2e/tests/deployment-logs.cy.js @@ -1,4 +1,4 @@ -// Copyright (C) 2025 by Posit Software, PBC. +// Copyright (C) 2026 by Posit Software, PBC. // Purpose: Verify deployment status and post-deployment UI elements. // - Deploys static content. diff --git a/test/e2e/tests/file-include-exclude.cy.js b/test/e2e/tests/file-include-exclude.cy.js index cb939c6fa..3aba63da5 100644 --- a/test/e2e/tests/file-include-exclude.cy.js +++ b/test/e2e/tests/file-include-exclude.cy.js @@ -1,4 +1,4 @@ -// Copyright (C) 2025 by Posit Software, PBC. +// Copyright (C) 2026 by Posit Software, PBC. // Purpose: Verify file include/exclude toggling in the project files tree. // - Creates a PCS deployment for fastapi-simple. diff --git a/test/e2e/tests/multi-deployment-switching.cy.js b/test/e2e/tests/multi-deployment-switching.cy.js index 30797c73a..dd76ebf79 100644 --- a/test/e2e/tests/multi-deployment-switching.cy.js +++ b/test/e2e/tests/multi-deployment-switching.cy.js @@ -1,4 +1,4 @@ -// Copyright (C) 2025 by Posit Software, PBC. +// Copyright (C) 2026 by Posit Software, PBC. // Purpose: Verify switching between multiple deployments. // - Creates a deployment for static content. diff --git a/test/e2e/tests/package-management.cy.js b/test/e2e/tests/package-management.cy.js index 8b6befcc4..25f6a9b18 100644 --- a/test/e2e/tests/package-management.cy.js +++ b/test/e2e/tests/package-management.cy.js @@ -1,4 +1,4 @@ -// Copyright (C) 2025 by Posit Software, PBC. +// Copyright (C) 2026 by Posit Software, PBC. // Purpose: Verify Python packages section displays detected packages. // - Creates a PCS deployment for fastapi-simple. diff --git a/test/e2e/tests/quarto-deployment.cy.js b/test/e2e/tests/quarto-deployment.cy.js index c9e9ebdd3..44a267425 100644 --- a/test/e2e/tests/quarto-deployment.cy.js +++ b/test/e2e/tests/quarto-deployment.cy.js @@ -1,4 +1,4 @@ -// Copyright (C) 2025 by Posit Software, PBC. +// Copyright (C) 2026 by Posit Software, PBC. // Purpose: Verify Quarto document deployment via PCS. // - Creates a deployment for a markdown-only Quarto doc (no Python/R engine). diff --git a/test/e2e/tests/redeployment.cy.js b/test/e2e/tests/redeployment.cy.js index 690dc27ee..92eeb9a6d 100644 --- a/test/e2e/tests/redeployment.cy.js +++ b/test/e2e/tests/redeployment.cy.js @@ -1,4 +1,4 @@ -// Copyright (C) 2025 by Posit Software, PBC. +// Copyright (C) 2026 by Posit Software, PBC. // Purpose: Verify redeployment workflow - deploy, modify content, redeploy. // - Deploys static content, captures initial bundle_id. From a3b00c353e67c1b84ee73960a6ac6d127716fae7 Mon Sep 17 00:00:00 2001 From: zackverham <96081108+zackverham@users.noreply.github.com> Date: Mon, 9 Mar 2026 15:49:27 -0400 Subject: [PATCH 3/5] Remove unnecessary skipIfConnectVersionBefore from non-deploying tests The version skip is only needed when actually deploying to Connect (for requires-python support). These tests only create configs and interact with the sidebar UI, so they don't need the version gate. Co-Authored-By: Claude Opus 4.6 --- test/e2e/tests/file-include-exclude.cy.js | 2 -- test/e2e/tests/multi-deployment-switching.cy.js | 2 -- test/e2e/tests/package-management.cy.js | 2 -- 3 files changed, 6 deletions(-) diff --git a/test/e2e/tests/file-include-exclude.cy.js b/test/e2e/tests/file-include-exclude.cy.js index 3aba63da5..8e1560358 100644 --- a/test/e2e/tests/file-include-exclude.cy.js +++ b/test/e2e/tests/file-include-exclude.cy.js @@ -25,8 +25,6 @@ describe("File Include/Exclude Section", () => { }); it("Toggle file inclusion via checkbox updates TOML config", () => { - cy.skipIfConnectVersionBefore("2025.03"); - cy.expectInitialPublisherState(); cy.createPCSDeployment( diff --git a/test/e2e/tests/multi-deployment-switching.cy.js b/test/e2e/tests/multi-deployment-switching.cy.js index dd76ebf79..6ae61ee12 100644 --- a/test/e2e/tests/multi-deployment-switching.cy.js +++ b/test/e2e/tests/multi-deployment-switching.cy.js @@ -23,8 +23,6 @@ describe("Multi-Deployment Switching Section", () => { }); it("Switch between two deployments via picker", () => { - cy.skipIfConnectVersionBefore("2025.03"); - cy.expectInitialPublisherState(); // Step 1: Create first deployment (static) diff --git a/test/e2e/tests/package-management.cy.js b/test/e2e/tests/package-management.cy.js index 25f6a9b18..b43a90f1d 100644 --- a/test/e2e/tests/package-management.cy.js +++ b/test/e2e/tests/package-management.cy.js @@ -23,8 +23,6 @@ describe("Package Management Section", () => { }); it("Python packages section shows detected packages", () => { - cy.skipIfConnectVersionBefore("2025.03"); - cy.expectInitialPublisherState(); cy.createPCSDeployment( From affdeb1ad0a40a2387f2580816d17efa34f4bb9d Mon Sep 17 00:00:00 2001 From: zackverham <96081108+zackverham@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:04:51 -0400 Subject: [PATCH 4/5] Fix Prettier formatting in commands.js and deployment-logs.cy.js Co-Authored-By: Claude Opus 4.6 --- test/e2e/support/commands.js | 4 +--- test/e2e/tests/deployment-logs.cy.js | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/test/e2e/support/commands.js b/test/e2e/support/commands.js index 82e1ef7c7..b02c27d07 100644 --- a/test/e2e/support/commands.js +++ b/test/e2e/support/commands.js @@ -860,9 +860,7 @@ Cypress.Commands.add("modifyFileInContainer", (containerPath, content) => { ) .then((result) => { if (result.exitCode !== 0) { - throw new Error( - `Failed to modify file in container: ${result.stderr}`, - ); + throw new Error(`Failed to modify file in container: ${result.stderr}`); } }); }); diff --git a/test/e2e/tests/deployment-logs.cy.js b/test/e2e/tests/deployment-logs.cy.js index c52cdb10d..9cf2f1b04 100644 --- a/test/e2e/tests/deployment-logs.cy.js +++ b/test/e2e/tests/deployment-logs.cy.js @@ -46,9 +46,7 @@ describe("Deployment Status Section", () => { ); // Verify "View Content" button is visible - cy.publisherWebview() - .findByText("View Content") - .should("be.visible"); + cy.publisherWebview().findByText("View Content").should("be.visible"); // Verify deployment record has expected fields cy.getPublisherTomlFilePaths(projectDir).then((filePaths) => { From dc0e7bfc16bfb9b0deac2162be31452c7bd514be Mon Sep 17 00:00:00 2001 From: zackverham <96081108+zackverham@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:30:36 -0400 Subject: [PATCH 5/5] Fix credential accumulation across spec files in initializeConnect Add resetCredentials() call before setAdminCredentials() so the credentials file is clean before appending. Without this, each spec file's before() hook appends a duplicate [credentials.admin-code-server] TOML table, causing the credential picker to fail after enough specs run. Co-Authored-By: Claude Opus 4.6 --- test/e2e/support/commands.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e/support/commands.js b/test/e2e/support/commands.js index b02c27d07..bc7d6cf75 100644 --- a/test/e2e/support/commands.js +++ b/test/e2e/support/commands.js @@ -13,6 +13,7 @@ import "./workbench"; // from the with-connect GitHub Action, which handles Connect startup and bootstrapping. Cypress.Commands.add("initializeConnect", () => { cy.clearupDeployments(); + cy.resetCredentials(); cy.setAdminCredentials(); });