feat(e2e): add basic e2e test suite

This commit is contained in:
Kstala 2023-11-21 09:45:12 +01:00
parent f952dc8d0b
commit bad0462bbf
15 changed files with 565 additions and 1 deletions

View File

@ -7,3 +7,4 @@ SHOPWARE_API_TYPE="store-api"
SHOPWARE_ACCESS_TOKEN=""
SHOPWARE_USE_SEO_URLS="true"
SHOPWARE_REVALIDATION_SECRET=""
BASE_E2E_URL=""

45
.github/workflows/e2e-tests.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: e2e
on:
deployment_status:
jobs:
run-e2e:
name: Playwright testing deployment ${{ github.event.deployment_status.target_url }}
if: github.event_name == 'deployment_status' && github.event.deployment_status.state == 'success' && !contains(github.event.deployment_status.target_url, 'frontends-docs')
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20
- run: corepack enable
- run: pnpm --version
- uses: actions/setup-node@v3
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: install
run: pnpm install --frozen-lockfile --prefer-offline
- name: Install dependencies with Playwright
run: |
pnpm playwright install --with-deps
- name: Run tests
run: pnpm run test:e2e
env:
BASE_E2E_URL: ${{ github.event.deployment_status.target_url }}
- name: Upload artifacts
uses: actions/upload-artifact@v3
if: ${{ failure() }}
with:
name: reports
path: apps/e2e-tests/reports/
retention-days: 7

View File

@ -1,6 +1,6 @@
# Next.js Commerce with Shopware
A Next.js 13 and App Router-ready ecommerce template featuring:
A Next.js 14 and App Router-ready ecommerce template featuring:
- Next.js App Router
- Optimized for SEO using Next.js's Metadata

5
e2e-tests/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

91
e2e-tests/package-lock.json generated Normal file
View File

@ -0,0 +1,91 @@
{
"name": "e2e-tests",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "e2e-tests",
"version": "1.0.0",
"license": "ISC",
"devDependencies": {
"@playwright/test": "^1.39.0",
"@types/node": "^20.8.10"
}
},
"node_modules/@playwright/test": {
"version": "1.39.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz",
"integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==",
"dev": true,
"dependencies": {
"playwright": "1.39.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=16"
}
},
"node_modules/@types/node": {
"version": "20.8.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz",
"integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/playwright": {
"version": "1.39.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz",
"integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==",
"dev": true,
"dependencies": {
"playwright-core": "1.39.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=16"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.39.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
"integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=16"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
}
}
}

19
e2e-tests/package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "e2e-tests",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@playwright/test": "^1.39.0",
"@types/node": "^20.8.10"
},
"dependencies": {
"dotenv": "^16.3.1",
"find-up": "^7.0.0"
},
"type": "module"
}

View File

@ -0,0 +1,13 @@
import { Page } from '@playwright/test';
export class AbstractPage {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
async wait(time) {
await this.page.waitForTimeout(time);
}
}

View File

@ -0,0 +1,27 @@
import { Page } from '@playwright/test';
export class CategoryPage {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
async changePage() {
await this.page.waitForLoadState('networkidle');
await this.page.getByLabel('Next page').click();
}
async checkCategoryFilter() {
await this.page.waitForLoadState('networkidle');
await this.page.getByRole('link', { name: 'Price: High to low' }).click();
}
}
// getByLabel('Pagination', { exact: true })
// getByLabel('Next page')
// getByRole('link', { name: 'Price: High to low' })
// getByRole('link', { name: 'Price: Low to high' })
// getByRole('link', { name: 'Latest arrivals' })
// getByRole('link', { name: 'Trending' })

View File

@ -0,0 +1,36 @@
import { Locator, Page } from '@playwright/test';
import { AbstractPage } from './AbstractPage';
export class HomePage extends AbstractPage {
//readonly page: Page
readonly sliderLocator: Locator;
constructor(page: Page) {
super(page);
this.sliderLocator = page.locator("ul[class='flex animate-carousel gap-4']");
}
async visitMainPage() {
await this.page.goto('/');
}
async openProductPage() {
this.page
.getByRole('link', { name: 'LIGHT CLOTH TAUPE BRIGHT LIGHT CLOTH TAUPE BRIGHT €139.00EUR' })
.click();
}
async openVariantsCartPage() {
await this.page.waitForLoadState('networkidle');
await this.page.goto('/product/LAVENDA-Product-Variants/SW20004');
}
async openCateoryPage() {
await this.page.getByRole('link', { name: 'Products' }).click();
}
async goToCmsPages() {
await this.page.getByRole('link', { name: 'Defective Product' }).click();
await this.page.waitForURL('**/Defective-Product');
}
}

View File

@ -0,0 +1,44 @@
import { expect, Locator, Page } from '@playwright/test';
export class ProductPage {
readonly page: Page;
readonly addToCartButton: Locator;
readonly variant: Locator;
readonly variantText: Locator;
readonly productOption: Locator;
readonly miniCartLink: Locator;
readonly productRemove: Locator;
constructor(page: Page) {
this.page = page;
this.addToCartButton = page.getByTestId('add-to-cart-button');
this.variant = page.getByTestId('product-variant');
this.variantText = page.getByTestId('product-variant-text');
this.productOption = page.getByTestId('cart-product-options');
this.miniCartLink = page.getByTestId('cart-button');
this.productRemove = page.getByTestId('product-remove-button');
}
async addToCart() {
await expect(async () => {
await this.page.getByLabel('Add to cart').waitFor();
await this.page.getByLabel('Add to cart').dispatchEvent('click');
await expect(this.page.locator('div').filter({ hasText: /^My Cart$/ })).toBeVisible();
}).toPass({
// Probe, wait 1s, probe, wait 2s, probe, wait 10s, probe, wait 10s, probe, .... Defaults to [100, 250, 500, 1000].
intervals: [1_000, 2_000, 10_000],
timeout: 60_000
});
}
async selectVariant() {
await this.page.getByRole('button', { name: 'M' }).click();
await this.page.waitForLoadState('networkidle');
await this.page.getByRole('button', { name: 'blue' }).click();
await this.page.waitForLoadState('networkidle');
}
// async changeProductVariant(){
// await
// }
}

View File

@ -0,0 +1,76 @@
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';
import dotenv from 'dotenv';
import { findUpSync } from 'find-up';
// export const findEnv = () => findUpSync(process.env.ENV_FILE || ".env");
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// dotenv.config({ path: findEnv() });
dotenv.config({ path: findUpSync(process.env.ENV_FILE || '.env') });
/**
* See https://playwright.dev/docs/test-configuration.
*/
const newLocal = 'http://localhost:3000';
const baseURL = process.env.BASE_E2E_URL || newLocal;
console.log('Running tests for: ', baseURL);
const config: PlaywrightTestConfig = {
testDir: './tests',
outputDir: './reports',
/* Maximum time one test can run for. */
timeout: 30 * 1000,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: process.env.CI ? 30000 : 5000
},
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: false, //!!process.env.CI
/* Retry on CI only */
retries: process.env.CI ? 2 : 1,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 4 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 0,
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'retain-on-failure',
testIdAttribute: 'data-testid',
baseURL
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome']
}
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox']
}
}
]
};
export default config;

115
e2e-tests/pnpm-lock.yaml generated Normal file
View File

@ -0,0 +1,115 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
dotenv:
specifier: ^16.3.1
version: 16.3.1
find-up:
specifier: ^7.0.0
version: 7.0.0
devDependencies:
'@playwright/test':
specifier: ^1.39.0
version: 1.39.0
'@types/node':
specifier: ^20.8.10
version: 20.8.10
packages:
/@playwright/test@1.39.0:
resolution: {integrity: sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==}
engines: {node: '>=16'}
hasBin: true
dependencies:
playwright: 1.39.0
dev: true
/@types/node@20.8.10:
resolution: {integrity: sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==}
dependencies:
undici-types: 5.26.5
dev: true
/dotenv@16.3.1:
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
engines: {node: '>=12'}
dev: false
/find-up@7.0.0:
resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==}
engines: {node: '>=18'}
dependencies:
locate-path: 7.2.0
path-exists: 5.0.0
unicorn-magic: 0.1.0
dev: false
/fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/locate-path@7.2.0:
resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
p-locate: 6.0.0
dev: false
/p-limit@4.0.0:
resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
yocto-queue: 1.0.0
dev: false
/p-locate@6.0.0:
resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
p-limit: 4.0.0
dev: false
/path-exists@5.0.0:
resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: false
/playwright-core@1.39.0:
resolution: {integrity: sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==}
engines: {node: '>=16'}
hasBin: true
dev: true
/playwright@1.39.0:
resolution: {integrity: sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==}
engines: {node: '>=16'}
hasBin: true
dependencies:
playwright-core: 1.39.0
optionalDependencies:
fsevents: 2.3.2
dev: true
/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
dev: true
/unicorn-magic@0.1.0:
resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
engines: {node: '>=18'}
dev: false
/yocto-queue@1.0.0:
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
engines: {node: '>=12.20'}
dev: false

View File

@ -0,0 +1,30 @@
import { expect, test } from '@playwright/test';
import { CategoryPage } from '../page-objects/CategoryPage';
import { HomePage } from '../page-objects/HomePage';
test.describe.only('Category pagination', () => {
let homePage: HomePage;
let categoryPage: CategoryPage;
// Before Hook
test.beforeEach(async ({ page }) => {
homePage = new HomePage(page);
categoryPage = new CategoryPage(page);
await homePage.visitMainPage();
});
test('Category pagination verification', async ({ page }) => {
await homePage.visitMainPage();
await homePage.openCateoryPage();
await categoryPage.changePage();
await expect(page).toHaveURL('/search/Products?page=2');
});
test('Category filters verification', async ({ page }) => {
await homePage.visitMainPage();
await homePage.openCateoryPage();
await categoryPage.checkCategoryFilter();
await expect(page).toHaveURL('/search/Products?sort=price-desc');
});
});

View File

@ -0,0 +1,27 @@
import { expect, test } from '@playwright/test';
import { HomePage } from '../page-objects/HomePage';
test.describe.only('CMS links', () => {
let homePage: HomePage;
// Before Hook
test.beforeEach(async ({ page }) => {
homePage = new HomePage(page);
await homePage.visitMainPage();
});
test('Footer CMS links verification', async ({ page }) => {
await homePage.visitMainPage();
await homePage.goToCmsPages();
await page.waitForLoadState('domcontentloaded');
await expect(page).toHaveTitle(
'Defective Product | Next.js Commerce with Shopware Composable Frontends'
);
});
test('Home page slider verification', async ({ page }) => {
await homePage.visitMainPage();
await page.locator("ul[class='flex animate-carousel gap-4']").isVisible();
});
});

View File

@ -0,0 +1,35 @@
import { expect, test } from '@playwright/test';
import { HomePage } from '../page-objects/HomePage';
import { ProductPage } from '../page-objects/ProductPage';
test.describe.only('add product to cart', () => {
let homePage: HomePage;
let productPage: ProductPage;
// Before Hook
test.beforeEach(async ({ page }) => {
homePage = new HomePage(page);
productPage = new ProductPage(page);
await homePage.visitMainPage();
});
test('Add to cart simple product', async ({ page }) => {
await homePage.visitMainPage();
await homePage.openProductPage();
await productPage.addToCart();
await expect(
page.getByText('LIGHT CLOTH TAUPE BRIGHTLIGHT CLOTH TAUPE BRIGHT€139.00EUR1')
).toBeVisible();
});
test('Add to cart variant product', async ({ page }) => {
await homePage.visitMainPage();
await homePage.openVariantsCartPage();
await productPage.selectVariant();
await productPage.addToCart();
await expect(
page.getByText('LAVENDA Product VariantsLAVENDA Product Variants€22.95EUR1')
).toBeVisible();
});
});