diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 29ddb1c570..4c138d14d8 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -1,30 +1,68 @@ -name: UI validation on prod +name: Running Playwright test on UI on: - workflow_dispatch: - + push: + branches: [main] + paths: + - 'static/nginxaas-azure/js/cost-calculator_v2.js' + - 'content/nginxaas-azure/billing/usage-and-cost-estimator.md' + pull_request: + paths: + - 'static/nginxaas-azure/js/cost-calculator_v2.js' + - 'content/nginxaas-azure/billing/usage-and-cost-estimator.md' +permissions: + contents: read jobs: + get-playwright-version: + name: Get Playwright Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-playwright-version.outputs.version }} + steps: + - name: Checkout code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Get version from package.json + id: get-playwright-version + run: | + version=$(jq -r '.devDependencies["@playwright/test"] // .dependencies["@playwright/test"]' package.json) + test -n "$version" || { echo "No @playwright/test version found in package.json"; exit 1; } + echo "version=$version" >> $GITHUB_OUTPUT + echo "Version: " $version run-playwright-tests: + name: Run Playwright + needs: get-playwright-version runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/playwright:v${{needs.get-playwright-version.outputs.version}}-jammy steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v6 + - name: Checkout code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup Hugo + uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3.0.0 with: - node-version: lts/* + hugo-version: '0.147.8' + extended: true + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 #v6.0.0 + with: + go-version: '1.24.6' - name: Install dependencies run: npm ci - - name: Install Playwright Browsers - run: npx playwright install --with-deps - name: Run Playwright tests - run: npx playwright test --retries=2 - - uses: actions/upload-artifact@v6 - if: ${{ !cancelled() }} + id: test-ui + # Check done to set the home variable. Workaround as browser is unable to launch if the $HOME folder isn't owned by the current user. + if: ${{ runner.os == 'Linux' }} + env: + HOME: /root + run: | + git config --global --add safe.directory /__w/nginx-hugo-theme/nginx-hugo-theme + cd tests && npx playwright test | tee output.log + if grep -q "failed" output.log; then + echo "Playwright tests failed. Please view the Playwright report to see full error." + exit 1 + fi + - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 + id: artifact-upload + if: ${{ !cancelled() && failure() && steps.test-ui.conclusion == 'failure' }} with: name: playwright-report path: tests/playwright-report/ - retention-days: 30 - - uses: actions/upload-artifact@v6 - if: ${{ !cancelled() }} - with: - name: test-results - path: tests/test-results/ - retention-days: 30 + retention-days: 3 diff --git a/tests/n4a-calculator.spec.ts b/tests/n4a-calculator.spec.ts deleted file mode 100644 index f027638c86..0000000000 --- a/tests/n4a-calculator.spec.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { expect, test } from "@playwright/test"; -import { handleConsentPopup, waitFor } from "./util"; - -/** - * Changes the input for a given form section by updating the value in the input with the given value multiplier, and checks if the given value locator changes. - * Sometimes it shouldn't, so use 'shouldValueChange' to denote if a change is expected or not. - * - * @param page - * @param formSection - * @param value - * @param valueMultiplier - * @param shouldValueChange - */ -async function updateInputs(page, formSection, value, valueMultiplier = 3, shouldValueChange = true) { - const inputs = await (formSection.locator("input")).all(); - const oldNcuEstimate = await value.textContent(); - - for(let i = 0; i < inputs.length; i++) { - const input = inputs.at(i); - const type = await input.getAttribute("type"); - if(input && type === "number") { - const value = Number(await input.inputValue()); - const newValue = value + (value * valueMultiplier); // Increase by some significant value - await input.fill(newValue.toString()); - await page.keyboard.press('Enter'); - } - else if(type === "checkbox") { - await input.check(); - } - - const newNcuEstimate = await value.textContent(); - if(shouldValueChange) { - expect(newNcuEstimate).not.toBe(oldNcuEstimate); - } - else { - expect(newNcuEstimate).toBe(oldNcuEstimate) - } - } -} - -/** - * Returns a random number between the given min and max, inclusive. - * - * @param min - * @param max - * @returns - */ -function getRandomNumber(min, max) { - // Enforce to be integer - min = Math.ceil(min); - max = Math.floor(max); - return Math.floor(Math.random() * (max - min + 1)) + min; -} - -test.describe("Testing for N4A calculator page", () => { - test.beforeEach(async ({ page }) => { - await page.goto("/nginxaas/azure/billing/usage-and-cost-estimator/"); - await page.waitForLoadState("load"); - await waitFor(async () => await handleConsentPopup(page)); - }); - - test("calculator renders", async ({ page }) => { - const header = page.getByTestId("calculator-section-heading"); - const content = page.getByTestId("calculator-section-content"); - - await expect(header).toBeVisible(); - await expect(content).toBeVisible(); - }); - - test("calculator values render", async ({ page }) => { - // Conjunction - If outputs are rendered, it is safe to say the inputs are rendered. - const ncuEstimateValue = page.getByTestId("ncuEstimateValue"); - const totalValue = page.getByTestId("total-value"); - - expect(await ncuEstimateValue.textContent()).toBeTruthy(); - expect(await totalValue.textContent()).toBeTruthy(); - }); - - test("inputs from 'Estimate NCU Usage' section change NCU Needed (happy)", async ({ page }) => { - const totalValue = page.getByTestId("total-value"); - const oldTotalValue = await totalValue.textContent(); - const ncuEstimateValue = page.getByTestId("ncuEstimateValue"); - const formSectionEstimateNCU = page.getByTestId("form-section-content-estimateNCUUsage"); - - // Inputs from NCU box should adjust estimate - await updateInputs(page, formSectionEstimateNCU, ncuEstimateValue, 80); - - // Check that total value changes - // Safe to say, if estimate NCU changes, so will the total monthly payment - const newTotalValue = await totalValue.textContent(); - expect(newTotalValue).not.toBe(oldTotalValue); - - }); - - test("inputs from 'Estimate NCU Usage' section change NCU Needed (unhappy 1)", async ({ page }) => { - const totalValue = page.getByTestId("total-value"); - const oldTotalValue = await totalValue.textContent(); - const ncuEstimateValue = page.getByTestId("ncuEstimateValue"); - const formSectionEstimateNCU = page.getByTestId("form-section-content-estimateNCUUsage"); - - // Inputs from NCU box should adjust estimate - await updateInputs(page, formSectionEstimateNCU, ncuEstimateValue, 0.1, false); - - // Check that total value doesn't changes - const newTotalValue = await totalValue.textContent(); - expect(newTotalValue).toBe(oldTotalValue); - }); - - test("inputs from 'Estimate NCU Usage' section change NCU Needed (unhappy 2)", async ({ page }) => { - const totalValue = page.getByTestId("total-value"); - const oldTotalValue = await totalValue.textContent(); - const ncuEstimateValue = page.getByTestId("ncuEstimateValue"); - const formSectionEstimateNCU = page.getByTestId("form-section-content-estimateNCUUsage"); - - // Inputs from NCU box should adjust estimate - await updateInputs(page, formSectionEstimateNCU, ncuEstimateValue, -0.1, false); - - // Check that total value doesn't changes - const newTotalValue = await totalValue.textContent(); - expect(newTotalValue).toBe(oldTotalValue); - }); - - test("inputs from 'Estimate Monthly Cost' section change Total Monthly Payment", async ({ page }) => { - const totalValue = page.getByTestId("total-value"); - const formSectionMonthlyCost = page.getByTestId("form-section-content-estimateMonthlyCost"); - - await updateInputs(page, formSectionMonthlyCost, totalValue); - }); - - test("'Listen Ports' input conditionally changes Total Monthly Payment", async({ page }) => { - const listenPorts = page.getByTestId("input-numListenPorts"); - const totalValue = page.getByTestId("total-value"); - const oldTotalValue = await totalValue.textContent(); - const randomMaxValue = getRandomNumber(5, 10); // Smaller max for performance reasons. - - for(let i = 1; i <= randomMaxValue; i++) { - await listenPorts.fill(i.toString()); - await page.keyboard.press('Enter'); - - // First 5 are included - const newTotalValue = await totalValue.textContent(); - if(i <= 5) { - expect(newTotalValue).toBe(oldTotalValue); - } - else { - expect(newTotalValue).not.toBe(oldTotalValue); - } - } - }); -}); diff --git a/playwright.config.ts b/tests/playwright.config.ts similarity index 86% rename from playwright.config.ts rename to tests/playwright.config.ts index 2606349390..e4101ae3ef 100644 --- a/playwright.config.ts +++ b/tests/playwright.config.ts @@ -1,10 +1,10 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ - testDir: 'tests', + testDir: './src', fullyParallel: true, workers: 1, - outputDir: 'tests/test-results', - reporter: [['html', { outputFolder: 'tests/playwright-report' }]], + outputDir: './test-results', + reporter: [['html', { outputFolder: './playwright-report' }]], projects: [ { name: 'chromium', diff --git a/tests/src/n4a-calculator.spec.ts b/tests/src/n4a-calculator.spec.ts new file mode 100644 index 0000000000..1520dcb50e --- /dev/null +++ b/tests/src/n4a-calculator.spec.ts @@ -0,0 +1,28 @@ +import { expect, test } from "@playwright/test"; +import { handleConsentPopup, waitFor } from "../util"; + +test.describe("Testing for N4A calculator page", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/nginxaas/azure/billing/usage-and-cost-estimator/"); + await page.waitForLoadState("load"); + await waitFor(async () => await handleConsentPopup(page)); + }); + + test("calculator renders", async ({ page }) => { + const header = page.getByTestId("calculator-section-heading"); + const content = page.getByTestId("calculator-section-content"); + + await expect(header).toBeVisible(); + await expect(content).toBeVisible(); + }); + + test("calculator values render", async ({ page }) => { + // Conjunction - If outputs are rendered, it is safe to say the inputs are rendered. + // NOT testing changing numbers will impact the total values as that should be the job of unit tests. This is just a smoke tests. + const ncuEstimateValue = page.getByTestId("ncuEstimateValue"); + const totalValue = page.getByTestId("total-value"); + + expect(await ncuEstimateValue.textContent()).toBeTruthy(); + expect(await totalValue.textContent()).toBeTruthy(); + }); +});