diff --git a/.changeset/poor-ducks-mate.md b/.changeset/poor-ducks-mate.md new file mode 100644 index 00000000000..38e55a6d52c --- /dev/null +++ b/.changeset/poor-ducks-mate.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": patch +--- + +Add manual test run for e2e diff --git a/.github/actions/merge-pw-reports/action.yml b/.github/actions/merge-pw-reports/action.yml new file mode 100644 index 00000000000..b646a9fe3f8 --- /dev/null +++ b/.github/actions/merge-pw-reports/action.yml @@ -0,0 +1,30 @@ +name: Merge playwright reports +description: Merge reports from all shards + +runs: + using: "composite" + steps: + - uses: actions/setup-node@v3 + with: + node-version-file: ".nvmrc" + + - name: Install dependencies + shell: bash + run: npm ci + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@v3 + with: + name: all-blob-reports + path: all-blob-reports + + - name: Merge into HTML Report + shell: bash + run: npx playwright merge-reports --reporter html ./all-blob-reports + + - name: Upload HTML report + uses: actions/upload-artifact@v3 + with: + name: html-report--attempt-${{ github.run_attempt }} + path: playwright-report + retention-days: 14 diff --git a/.github/actions/prepare-api-variables/action.yml b/.github/actions/prepare-api-variables/action.yml new file mode 100644 index 00000000000..afc1bcd1bf3 --- /dev/null +++ b/.github/actions/prepare-api-variables/action.yml @@ -0,0 +1,73 @@ +name: Prepare api variables +description: Prepare env config for cloud + +inputs: + MODE: + description: "The mode of running tests (pull-request, release, main)" + required: true + +outputs: + BASE_URL: + description: "Dashboard base url" + value: ${{ steps.generate.outputs.BASE_URL }} + API_URL: + description: "API url" + value: ${{ steps.generate.outputs.API_URL }} + POOL_NAME: + description: "The name of the instance" + value: ${{ steps.generate.outputs.POOL_NAME }} + POOL_INSTANCE: + description: "The full URL of the instance" + value: ${{ steps.generate.outputs.POOL_INSTANCE }} + BACKUP_NAMESPACE: + description: "The backup namespace" + value: ${{ steps.generate.outputs.BACKUP_NAMESPACE }} + +runs: + using: "composite" + steps: + - name: Inject slug/short variables + uses: rlespinasse/github-slug-action@102b1a064a9b145e56556e22b18b19c624538d94 + + - name: Generate + id: generate + shell: bash + env: + MODE: ${{ inputs.MODE }} + PREFIX: pr- + run: | + if [[ "$MODE" == 'pull-request' ]]; then + echo "BASE_URL=${PREFIX}${GITHUB_HEAD_REF_SLUG_URL}.dashboard.saleor.rocks" >> $GITHUB_OUTPUT + echo "API_URL=https://${PREFIX}${GITHUB_HEAD_REF_SLUG_URL}.staging.saleor.cloud/graphql/" >> $GITHUB_OUTPUT + echo "POOL_NAME=${PREFIX}${GITHUB_HEAD_REF_SLUG_URL}" >> $GITHUB_OUTPUT + echo "POOL_INSTANCE=https://${PREFIX}${GITHUB_HEAD_REF_SLUG_URL}.staging.saleor.cloud" >> $GITHUB_OUTPUT + echo "BACKUP_NAMESPACE=snapshot-automation-tests" >> $GITHUB_OUTPUT + + exit 0 + fi + + if [[ "$MODE" == 'release' ]]; then + VERSION_SLUG=$(echo "${GITHUB_REF_SLUG}" | sed "s/\.//") + + echo "BASE_URL=https://v${VERSION_SLUG}.staging.saleor.cloud/dashboard/" >> $GITHUB_OUTPUT + echo "API_URL=https://v${VERSION_SLUG}.staging.saleor.cloud/graphql/" >> $GITHUB_OUTPUT + echo "POOL_NAME=${GITHUB_REF_SLUG} Staging" >> $GITHUB_OUTPUT + echo "POOL_INSTANCE=https://v${VERSION_SLUG}.staging.saleor.cloud/" >> $GITHUB_OUTPUT + echo "BACKUP_NAMESPACE=snapshot-automation-tests-${GITHUB_REF_SLUG}" >> $GITHUB_OUTPUT + + exit 0 + fi + + if [[ "$MODE" == 'main' ]]; then + echo "BASE_URL=https://automation-dashboard.staging.saleor.cloud/dashboard/" >> $GITHUB_OUTPUT + echo "API_URL=https://automation-dashboard.staging.saleor.cloud/graphql/" >> $GITHUB_OUTPUT + echo "POOL_NAME=automation-dashboard" >> $GITHUB_OUTPUT + echo "POOL_INSTANCE=https://automation-dashboard.staging.saleor.cloud" >> $GITHUB_OUTPUT + echo "BACKUP_NAMESPACE=snapshot-automation-tests" >> $GITHUB_OUTPUT + + exit 0 + fi + + echo "Unknown mode: $MODE" >&2 + exit 1 + diff --git a/.github/actions/prepare-backups-variables/action.yml b/.github/actions/prepare-backups-variables/action.yml new file mode 100644 index 00000000000..c46c3457a74 --- /dev/null +++ b/.github/actions/prepare-backups-variables/action.yml @@ -0,0 +1,42 @@ +name: Prepare backup variables +description: Prepare database snapshots +inputs: + CLI_TOKEN: + description: "Saleor cli token" + required: true + BACKUP_NAMESPACE: + description: "The backups namespace" + required: true +outputs: + BACKUP_ID: + description: "The ID of backup" + value: ${{ steps.backup.outputs.BACKUP_ID }} + BACKUP_VER: + description: "The version of backup" + value: ${{ steps.backup.outputs.BACKUP_VER }} + BACKUP_NAME: + description: "The name of backup" + value: ${{ steps.backup.outputs.BACKUP_NAME }} +runs: + using: "composite" + steps: + - name: Saleor login + uses: ./.github/actions/cli-login + with: + token: ${{ inputs.CLI_TOKEN }} + + - name: Obtain backup id + id: backup + shell: bash + env: + SALEOR_CLI_ENV: staging + BACKUP_NAME: ${{ inputs.BACKUP_NAMESPACE }} + run: | + BACKUPS=$(npx saleor backup list --name="$BACKUP_NAME" --latest --json) + BACKUP_ID=$(echo "$BACKUPS" | jq -r '.[0].key') + BACKUP_VER=$(echo "$BACKUPS" | jq -r '.[0].saleor_version') + BACKUP_NAME=$(echo "$BACKUPS" | jq -r '.[0].name') + + echo "BACKUP_ID=$BACKUP_ID" >> $GITHUB_OUTPUT + echo "BACKUP_VER=$BACKUP_VER" >> $GITHUB_OUTPUT + echo "BACKUP_NAME=$BACKUP_NAME" >> $GITHUB_OUTPUT diff --git a/.github/actions/prepare-instance/action.yml b/.github/actions/prepare-instance/action.yml new file mode 100644 index 00000000000..6b8efed3fd4 --- /dev/null +++ b/.github/actions/prepare-instance/action.yml @@ -0,0 +1,73 @@ +name: Prepare instance +description: Prepare cloud instance for testing +inputs: + STRATEGY: + description: "How to create the new instance (create|reload)" + required: false + default: "create" + CLI_TOKEN: + description: "Saleor cli token" + required: true + BASE_URL: + description: "Dashboard base url" + required: true + API_URL: + description: "API url" + required: true + POOL_NAME: + description: "The name of the instance" + required: true + POOL_INSTANCE: + description: "The full URL of the instance" + required: true + BACKUP_ID: + description: "The id of backup" + required: true +runs: + using: "composite" + steps: + - name: Saleor login + uses: ./.github/actions/cli-login + with: + token: ${{ inputs.CLI_TOKEN }} + + - name: Instance check + if: ${{ inputs.STRATEGY == 'reload' }} + shell: bash + id: instance_check + env: + SALEOR_CLI_ENV: staging + INSTANCE_NAME: ${{ inputs.POOL_NAME }} + run: | + set +o pipefail + INSTANCE_KEY=$(npx saleor env show "$INSTANCE_NAME" --json | jq .key) + echo "INSTANCE_KEY=$INSTANCE_KEY" >> $GITHUB_OUTPUT + + - name: Reload snapshot + shell: bash + if: ${{ steps.instance_check.outputs.INSTANCE_KEY }} + env: + SALEOR_CLI_ENV: staging + BACKUP_ID: ${{ inputs.BACKUP_ID }} + INSTANCE_NAME: ${{ inputs.POOL_NAME }} + run: | + npx saleor backup restore "$BACKUP_ID" \ + --environment="$INSTANCE_NAME" \ + --skip-webhooks-update + + - name: Create new instance + shell: bash + if: ${{ !steps.instance_check.outputs.INSTANCE_KEY }} + env: + SALEOR_CLI_ENV: staging + BACKUP_ID: ${{ inputs.BACKUP_ID }} + INSTANCE_NAME: ${{ inputs.POOL_NAME }} + run: | + npx saleor env create "$INSTANCE_NAME" \ + --project=project-for-pr-testing \ + --database=snapshot \ + --restore-from="$BACKUP_ID" \ + --saleor=saleor-master-staging \ + --domain="$INSTANCE_NAME" \ + --skip-restrict \ + --skip-webhooks-update \ No newline at end of file diff --git a/.github/actions/prepare-tests-variables/action.yml b/.github/actions/prepare-tests-variables/action.yml new file mode 100644 index 00000000000..dc38f8f57e7 --- /dev/null +++ b/.github/actions/prepare-tests-variables/action.yml @@ -0,0 +1,68 @@ +name: Prepare tests variables +description: Prepare database snapshots +inputs: + MODE: + description: "The mode of running tests (pull-request, release, main)" + required: true + CLI_TOKEN: + description: "Saleor cli token" + required: true +outputs: + BASE_URL: + description: "Dashboard base url" + value: ${{ steps.api.outputs.BASE_URL }} + API_URL: + description: "API url" + value: ${{ steps.api.outputs.API_URL }} + POOL_NAME: + description: "The name of the instance" + value: ${{ steps.api.outputs.POOL_NAME }} + POOL_INSTANCE: + description: "The full URL of the instance" + value: ${{ steps.api.outputs.POOL_INSTANCE }} + BACKUP_ID: + description: "The ID of backup" + value: ${{ steps.backup.outputs.BACKUP_ID }} + BACKUP_VER: + description: "The version of backup" + value: ${{ steps.backup.outputs.BACKUP_VER }} + BACKUP_NAME: + description: "The name of backup" + value: ${{ steps.backup.outputs.BACKUP_NAME }} + BACKUP_NAMESPACE: + description: "The name of backup" + value: ${{ steps.api.outputs.BACKUP_NAMESPACE }} +runs: + using: "composite" + steps: + - name: Generate api + id: api + uses: ./.github/actions/prepare-api-variables + with: + MODE: ${{ inputs.MODE }} + + - name: Generate backup + id: backup + uses: ./.github/actions/prepare-backups-variables + with: + CLI_TOKEN: ${{ inputs.CLI_TOKEN }} + BACKUP_NAMESPACE: ${{ steps.api.outputs.BACKUP_NAMESPACE }} + + - name: Print annotations + shell: bash + env: + BASE_URL: ${{ steps.api.outputs.BASE_URL }} + API_URL: ${{ steps.api.outputs.API_URL }} + POOL_NAME: ${{ steps.api.outputs.POOL_NAME }} + POOL_INSTANCE: ${{ steps.api.outputs.POOL_INSTANCE }} + BACKUP_ID: ${{ steps.backup.outputs.BACKUP_ID }} + BACKUP_VER: ${{ steps.backup.outputs.BACKUP_VER }} + BACKUP_NAME: ${{ steps.backup.outputs.BACKUP_NAME }} + BACKUP_NAMESPACE: ${{ steps.api.outputs.BACKUP_NAMESPACE }} + run: | + echo "::notice title=BASE_URL::${BASE_URL}" + echo "::notice title=API_URL::${API_URL}" + echo "::notice title=POOL_NAME::${POOL_NAME}" + echo "::notice title=POOL_INSTANCE::${POOL_INSTANCE}" + echo "::notice title=BACKUP_NAMESPACE::${BACKUP_NAMESPACE}" + echo "::notice title=SNAPSHOT::backup_id=${BACKUP_ID}, version=${BACKUP_VER}, name=${BACKUP_NAME}" \ No newline at end of file diff --git a/.github/actions/run-pw-tests/action.yml b/.github/actions/run-pw-tests/action.yml new file mode 100644 index 00000000000..c5300bc0372 --- /dev/null +++ b/.github/actions/run-pw-tests/action.yml @@ -0,0 +1,54 @@ +name: Run Playwright tests +description: Run e2e tests using Playwright +inputs: + BASE_URL: + description: "Dashboard base url" + required: true + API_URL: + description: "API url" + required: true + E2E_USER_NAME: + description: "Username for e2e tests" + required: true + E2E_USER_PASSWORD: + description: "Password for e2e tests" + required: true + E2E_PERMISSIONS_USERS_PASSWORD: + description: "Permissions user password for e2e tests" + required: true + SHARD: + description: "Shard number" + required: true +runs: + using: "composite" + steps: + - uses: actions/setup-node@v3 + with: + node-version-file: ".nvmrc" + + - name: Install dependencies + shell: bash + run: npm ci + + - name: Install Playwright Browsers + shell: bash + run: npx playwright install --with-deps + + - name: Run tests + shell: bash + env: + API_URI: ${{ inputs.API_URL }} + BASE_URL: ${{ inputs.BASE_URL }} + E2E_USER_NAME: ${{ inputs.E2E_USER_NAME }} + E2E_USER_PASSWORD: ${{ inputs.E2E_USER_PASSWORD }} + E2E_PERMISSIONS_USERS_PASSWORD: ${{ inputs.E2E_PERMISSIONS_USERS_PASSWORD }} + SHARD_NUMBER: ${{ inputs.SHARD }} + run: npm run qa:pw-e2e -- --shard "$SHARD_NUMBER" + + - name: Upload blob report to GitHub Actions Artifacts + uses: actions/upload-artifact@v3 + if: always() + with: + name: all-blob-reports + path: blob-report + retention-days: 1 diff --git a/.github/workflows/run-test-manual.yml b/.github/workflows/run-test-manual.yml new file mode 100644 index 00000000000..eb3f1fbc144 --- /dev/null +++ b/.github/workflows/run-test-manual.yml @@ -0,0 +1,82 @@ +name: Run test manually + +on: [workflow_dispatch] + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +jobs: + initialize-cloud: + runs-on: ubuntu-22.04 + outputs: + POOL_NAME: ${{ steps.pool_variables.outputs.POOL_NAME }} + POOL_INSTANCE: ${{ steps.pool_variables.outputs.POOL_INSTANCE }} + BASE_URL: ${{ steps.cloud_variables.outputs.BASE_URL }} + API_URL: ${{ steps.cloud_variables.outputs.API_URL }} + BACKUP_ID: ${{ steps.cloud_variables.outputs.BACKUP_ID }} + BACKUP_VER: ${{ steps.cloud_variables.outputs.BACKUP_VER }} + BACKUP_NAME: ${{ steps.cloud_variables.outputs.BACKUP_NAME }} + steps: + - uses: actions/checkout@v4 + with: + sparse-checkout: ./.github/actions + + - name: Set variables mode + id: set_variables_mode + shell: bash + run: | + if [[ "$GITHUB_REF" == "refs/heads/main" ]]; then + echo "MODE=main" >> $GITHUB_OUTPUT + else + echo "MODE=release" >> $GITHUB_OUTPUT + fi + + - name: Generate variables + id: cloud_variables + uses: ./.github/actions/prepare-tests-variables + with: + CLI_TOKEN: ${{ secrets.STAGING_TOKEN }} + MODE: ${{ steps.set_variables_mode.outputs.MODE }} + + - name: Prepare instances + id: pool_variables + uses: ./.github/actions/prepare-instance + with: + STRATEGY: reload + CLI_TOKEN: ${{ secrets.STAGING_TOKEN }} + BASE_URL: ${{ steps.cloud_variables.outputs.BASE_URL }} + API_URL: ${{ steps.cloud_variables.outputs.API_URL }} + POOL_NAME: ${{ steps.cloud_variables.outputs.POOL_NAME }} + POOL_INSTANCE: ${{ steps.cloud_variables.outputs.POOL_INSTANCE }} + BACKUP_ID: ${{ steps.cloud_variables.outputs.BACKUP_ID }} + + run-tests: + runs-on: ubuntu-22.04 + needs: initialize-cloud + strategy: + fail-fast: false + matrix: + shard: [1/2, 2/2] + steps: + - uses: actions/checkout@v4 + + - name: Run playwright tests + uses: ./.github/actions/run-pw-tests + with: + SHARD: ${{ matrix.shard }} + BASE_URL: ${{ needs.initialize-cloud.outputs.BASE_URL }} + API_URL: ${{ needs.initialize-cloud.outputs.API_URL }} + E2E_USER_NAME: ${{ secrets.CYPRESS_USER_NAME }} + E2E_USER_PASSWORD: ${{ secrets.CYPRESS_USER_PASSWORD }} + E2E_PERMISSIONS_USERS_PASSWORD: ${{ secrets.CYPRESS_PERMISSIONS_USERS_PASSWORD }} + + merge-reports: + if: '!cancelled()' + needs: run-tests + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Merge playwright reports + uses: ./.github/actions/merge-pw-reports diff --git a/playwright/data/url.ts b/playwright/data/url.ts index 2a5a7a85acc..a724600a0af 100644 --- a/playwright/data/url.ts +++ b/playwright/data/url.ts @@ -12,22 +12,22 @@ export const URL_LIST = { dashboard: "dashboard/", draftOrders: "orders/drafts/", giftCards: "gift-cards/", - homePage: "/", - newPassword: "/new-password/", - navigation: "/navigation/", - orders: "/orders/", - pages: "/pages/", - pageTypes: "/page-types/", - permissionsGroups: "/permission-groups/", - plugins: "/plugins/", - products: "/products/", + homePage: "", + newPassword: "new-password/", + navigation: "navigation/", + orders: "orders/", + pages: "pages/", + pageTypes: "page-types/", + permissionsGroups: "permission-groups/", + plugins: "plugins/", + products: "products/", productsAdd: "add?product-type-id=", - productTypes: "/product-types/", - productTypesAdd: "/product-types/add", - sales: "/discounts/sales/", - shippingMethods: "/shipping/", - siteSettings: "/site-settings/", - staffMembers: "/staff/", + productTypes: "product-types/", + productTypesAdd: "product-types/add", + sales: "discounts/sales/", + shippingMethods: "shipping/", + siteSettings: "site-settings/", + staffMembers: "staff/", stripeApiPaymentMethods: "https://api.stripe.com/v1/payment_methods", translations: "translations/", variants: "variant/", @@ -36,5 +36,5 @@ export const URL_LIST = { variant: "variant/", warehouses: "warehouses/", webhooksAndEvents: "custom-apps/", - resetPassword: "/new-password/?email=", + resetPassword: "new-password/?email=", }; diff --git a/playwright/pages/homePage.ts b/playwright/pages/homePage.ts index c9e2689a758..b57e386c4a8 100644 --- a/playwright/pages/homePage.ts +++ b/playwright/pages/homePage.ts @@ -1,3 +1,4 @@ +import { URL_LIST } from "@data/url"; import { expect, Locator, Page } from "@playwright/test"; export class HomePage { @@ -27,7 +28,7 @@ export class HomePage { this.productsOutOfStock = page.getByTestId("out-of-stock-analytics"); } async goto() { - await this.page.goto("/"); + await this.page.goto(URL_LIST.homePage); } async clickChannelSelectButton() { diff --git a/playwright/tests/auth.setup.ts b/playwright/tests/auth.setup.ts index a9947b65751..b6600877154 100644 --- a/playwright/tests/auth.setup.ts +++ b/playwright/tests/auth.setup.ts @@ -1,3 +1,4 @@ +import { URL_LIST } from "@data/url"; import { USER_PERMISSION } from "@data/userPermissions"; import { LoginPage } from "@pages/loginPage"; import { test as setup } from "@playwright/test"; @@ -32,7 +33,7 @@ setup("authenticate as admin", async ({ page }) => { }); setup("unauthenticated user ", async ({ page }) => { const loginPage = await new LoginPage(page); - await page.goto("/"); + await page.goto(URL_LIST.homePage); await loginPage.resetPasswordLink.waitFor({ state: "visible" }); // End of authentication steps. await page diff --git a/playwright/tests/discountAndVouchers.spec.ts b/playwright/tests/discountAndVouchers.spec.ts index fe807829e9d..ee46e0b9705 100644 --- a/playwright/tests/discountAndVouchers.spec.ts +++ b/playwright/tests/discountAndVouchers.spec.ts @@ -215,7 +215,8 @@ test("TC: SALEOR_93 Bulk delete voucher @vouchers @e2e", async () => { ).toEqual([]); }); -test("TC: SALEOR_94 Edit voucher - assign voucher to specific category @vouchers @e2e", async () => { +// TODO: https://github.com/saleor/saleor-dashboard/issues/4590 +test.skip("TC: SALEOR_94 Edit voucher - assign voucher to specific category @vouchers @e2e", async () => { const categoryToBeAssigned = "Accessories"; await vouchersPage.gotoExistingVoucherPage( diff --git a/playwright/tests/home.spec.ts b/playwright/tests/home.spec.ts index 23a3278e134..01e0ab0b9fc 100644 --- a/playwright/tests/home.spec.ts +++ b/playwright/tests/home.spec.ts @@ -1,3 +1,4 @@ +import { URL_LIST } from "@data/url"; import { HomePage } from "@pages/homePage"; import { expect, test } from "@playwright/test"; @@ -8,7 +9,7 @@ test("TC: SALEOR_29 Correct information on dashboard home page @e2e", async ({ }) => { const homePage = new HomePage(page); - await page.goto("/"); + await page.goto(URL_LIST.homePage); await expect(homePage.channelSelect).toBeVisible({ timeout: 10000 }); await homePage.expectHomePageElementsToBeVisible();