diff --git a/.gitignore b/.gitignore index f0a5f938..a194e9bf 100644 --- a/.gitignore +++ b/.gitignore @@ -906,3 +906,7 @@ FodyWeavers.xsd # Additional files built by Visual Studio # End of https://www.toptal.com/developers/gitignore/api/csharp,visualstudio,aspnetcore,visualstudiocode +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/README.md b/README.md index 48d27a88..3c76037b 100644 --- a/README.md +++ b/README.md @@ -69,20 +69,20 @@ Build an e-commerce web site with minimum functionality below: ## Technical Stack -- [ASP.NET Core 8.0](https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-8.0) - [htmx](https://htmx.org/) - [Alphine.js](https://alpinejs.dev/) - [///\_hyperscript](https://hyperscript.org/) - [Next.js 14.0](https://nextjs.org/) +- [ASP.NET Core 8.0](https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-8.0) - [Duende IdentityServer 7.0](https://duendesoftware.com/products/identityserver) - [Redis](https://redis.io/) - [Postgres](https://www.postgresql.org/) - [Aspire](https://learn.microsoft.com/dotnet/aspire) - [Yarp](https://microsoft.github.io/reverse-proxy/) - [OpenTelemetry](https://opentelemetry.io/) -- [NUKE](https://nuke.build/) -- [Semantic Kernel](aka.ms/semantic-kernel) - [OpenAI](https://openai.com/) +- [Semantic Kernel](aka.ms/semantic-kernel) +- [NUKE](https://nuke.build/) ## Software Architecture @@ -106,11 +106,11 @@ Build an e-commerce web site with minimum functionality below: - Get the latest source code: [https://github.com/foxminchan/RookieShop](https://github.com/foxminchan/RookieShop) - Install & start Docker Desktop: [https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/) +- Install k3d: [https://k3d.io/](https://k3d.io/) - Install .NET 8.0 SDK: [https://dotnet.microsoft.com/download](https://dotnet.microsoft.com/download) - Install Node.js: [https://nodejs.org/en/download/](https://nodejs.org/en/download/) - Install bun: [https://bun.sh/](https://bun.sh/) - Install Stripe CLI: [https://stripe.com/docs/stripe-cli](https://stripe.com/docs/stripe-cli) -- Install k3d: [https://k3d.io/](https://k3d.io/) - Open AI API Key: [https://platform.openai.com/](https://platform.openai.com/) #### Windows with Visual Studio @@ -130,7 +130,7 @@ Build an e-commerce web site with minimum functionality below: ```bash # Setup the tools -npm install +npm install --force dotnet tool restore # Install the dependencies for the .NET Core projects diff --git a/bun.lockb b/bun.lockb deleted file mode 100644 index 49a79801..00000000 Binary files a/bun.lockb and /dev/null differ diff --git a/e2e/login.setup.ts b/e2e/login.setup.ts new file mode 100644 index 00000000..e69de29b diff --git a/package-lock.json b/package-lock.json index 422fc0b2..efb53875 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,10 @@ "devDependencies": { "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", + "@playwright/test": "^1.44.1", + "@types/node": "^20.14.2", "@vuepress/bundler-vite": "2.0.0-rc.12", + "dotenv": "^16.4.5", "husky": "^9.0.11", "vue": "^3.4.27", "vuepress": "2.0.0-rc.12", @@ -1315,6 +1318,21 @@ "node": ">= 8" } }, + "node_modules/@playwright/test": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", + "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "dev": true, + "dependencies": { + "playwright": "1.44.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", @@ -2571,9 +2589,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", "dev": true, "funding": [ { @@ -2590,10 +2608,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "update-browserslist-db": "^1.0.16" }, "bin": { "browserslist": "cli.js" @@ -3101,6 +3119,18 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.796", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.796.tgz", @@ -3380,9 +3410,9 @@ } }, "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "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, @@ -3886,9 +3916,9 @@ } }, "node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, "engines": { "node": ">=14" @@ -4437,6 +4467,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", + "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "dev": true, + "dependencies": { + "playwright-core": "1.44.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", + "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/pngjs": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", @@ -5341,6 +5401,20 @@ } } }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/vue": { "version": "3.4.27", "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz", @@ -5903,9 +5977,9 @@ } }, "node_modules/yaml": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.3.tgz", - "integrity": "sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", "dev": true, "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index 0cefbf51..6ad43887 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,10 @@ "devDependencies": { "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", + "@playwright/test": "^1.44.1", + "@types/node": "^20.14.2", "@vuepress/bundler-vite": "2.0.0-rc.12", + "dotenv": "^16.4.5", "husky": "^9.0.11", "vue": "^3.4.27", "vuepress": "2.0.0-rc.12", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..cebcc2ab --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,86 @@ +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. + */ +export default defineConfig({ + testDir: "./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: "http://localhost:5045", + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + ...devices["Desktop Chrome"], + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: "setup", + testMatch: "**/*.setup.ts", + }, + // { + // 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: + "dotnet run --project src/RookieShop.AppHost/RookieShop.AppHost.csproj", + url: "http://localhost:5045", + reuseExistingServer: !process.env.CI, + stderr: "pipe", + stdout: "pipe", + timeout: process.env.CI ? 5 * 60_000 : 60_000, + }, +});