diff --git a/.gitignore b/.gitignore index c7ad28f..ca26206 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,7 @@ next-env.d.ts robots.txt sitemap.xml sitemap-*.xml +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/jest.config.js b/jest.config.js index c75d356..a6adaff 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,6 +7,7 @@ const createJestConfig = nextJest({ const config = { setupFilesAfterEnv: ['/jest.setup.js'], testEnvironment: 'jest-environment-jsdom', + testPathIgnorePatterns: ['/src/__tests__/e2e'], }; module.exports = createJestConfig(config); diff --git a/package-lock.json b/package-lock.json index 4e3f134..ff2c8f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "next-starter", - "version": "0.1.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "next-starter", - "version": "0.1.0", + "version": "1.0.0", "dependencies": { "@radix-ui/react-slot": "^1.0.2", "class-variance-authority": "^0.7.0", - "clsx": "2.1.0", + "clsx": "^2.1.0", "lucide-react": "^0.312.0", "next": "14.1.0", "next-themes": "^0.2.1", @@ -21,6 +21,7 @@ "devDependencies": { "@commitlint/cli": "^18.6.0", "@commitlint/config-conventional": "^18.6.0", + "@playwright/test": "^1.41.1", "@testing-library/jest-dom": "^6.3.0", "@testing-library/react": "^14.1.2", "@types/jest": "^29.5.11", @@ -41,7 +42,7 @@ "jest-environment-jsdom": "^29.7.0", "lint-staged": "^15.2.0", "next-sitemap": "^4.2.3", - "postcss": "8.4.33", + "postcss": "^8", "prettier": "^3.2.4", "tailwindcss": "^3.3.0", "tailwindcss-animate": "^1.0.7", @@ -2164,6 +2165,21 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@playwright/test": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.1.tgz", + "integrity": "sha512-9g8EWTjiQ9yFBXc6HjCWe41msLpxEX0KhmfmPl9RPLJdfzL4F0lg2BdJ91O9azFdl11y1pmpwdjBiSxvqc+btw==", + "dev": true, + "dependencies": { + "playwright": "1.41.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", @@ -8882,6 +8898,50 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.1.tgz", + "integrity": "sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ==", + "dev": true, + "dependencies": { + "playwright-core": "1.41.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.1.tgz", + "integrity": "sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/playwright/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/postcss": { "version": "8.4.33", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", diff --git a/package.json b/package.json index 520e9a7..56d40d4 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "typecheck": "tsc --noEmit", "test": "jest", "test:watch": "jest --watchAll", + "e2e": "playwright test", + "e2e:ui": "playwright test --ui", "format:write": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache", "format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache", "postbuild": "next-sitemap --config next-sitemap.config.js", @@ -42,6 +44,7 @@ "devDependencies": { "@commitlint/cli": "^18.6.0", "@commitlint/config-conventional": "^18.6.0", + "@playwright/test": "^1.41.1", "@testing-library/jest-dom": "^6.3.0", "@testing-library/react": "^14.1.2", "@types/jest": "^29.5.11", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..71be79f --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,83 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ + +// Use process.env.PORT by default and fallback to port 3000 +const PORT = process.env.PORT || 3000; + +// Set webServer.url and use.baseURL with the location of the WebServer respecting the correct set port +const baseURL = `http://localhost:${PORT}`; + +export default defineConfig({ + testDir: './src/__tests__/e2e', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : 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: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL, + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/src/__tests__/e2e/home.spec.ts b/src/__tests__/e2e/home.spec.ts new file mode 100644 index 0000000..fa3328e --- /dev/null +++ b/src/__tests__/e2e/home.spec.ts @@ -0,0 +1,7 @@ +import { expect, test } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('/'); + + await expect(page).toHaveTitle(/Next.js Starter/); +}); diff --git a/src/__tests__/home.test.tsx b/src/__tests__/unit/home.spec.tsx similarity index 100% rename from src/__tests__/home.test.tsx rename to src/__tests__/unit/home.spec.tsx