Skip to content

Commit fb8a612

Browse files
Merge pull request #182 from microsoft/chrajase/test-ci-cd
Add CI/CD basic E2E tests that run on PR/merge
2 parents be70bb9 + 5f86cbd commit fb8a612

9 files changed

Lines changed: 342 additions & 0 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Test Starter Template
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'templates/starter/**'
8+
- 'tests/**'
9+
pull_request:
10+
branches: [main]
11+
paths:
12+
- 'templates/starter/**'
13+
- 'tests/**'
14+
15+
jobs:
16+
test-starter-template:
17+
uses: ./.github/workflows/test-template.yml
18+
with:
19+
template: starter
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Test Template(s)
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
template:
7+
required: true
8+
type: string
9+
description: 'Template name (e.g., vite, starter)'
10+
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: '22'
21+
22+
- name: Install dependencies (tests)
23+
working-directory: tests
24+
run: npm ci
25+
26+
- name: Install dependencies (template)
27+
working-directory: templates/${{ inputs.template }}
28+
run: npm install
29+
30+
- name: Get Playwright version
31+
id: playwright-version
32+
working-directory: tests
33+
run: echo "version=$(npm ls @playwright/test --json | jq -r '.dependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT
34+
35+
- name: Cache Playwright browsers
36+
uses: actions/cache@v4
37+
id: playwright-cache
38+
with:
39+
path: ~/.cache/ms-playwright
40+
key: playwright-${{ steps.playwright-version.outputs.version }}
41+
42+
- name: Install Playwright browsers
43+
if: steps.playwright-cache.outputs.cache-hit != 'true'
44+
working-directory: tests
45+
run: npx playwright install --with-deps chromium
46+
47+
- name: Install Playwright system dependencies
48+
if: steps.playwright-cache.outputs.cache-hit == 'true'
49+
run: npx playwright install-deps chromium
50+
51+
- name: Run Playwright tests
52+
working-directory: tests
53+
run: npm run test
54+
env:
55+
CI: true
56+
TEMPLATE: ${{ inputs.template }}
57+
58+
- name: Upload Playwright report
59+
uses: actions/upload-artifact@v4
60+
if: ${{ !cancelled() }}
61+
with:
62+
name: playwright-report-${{ inputs.template }}-template
63+
path: tests/playwright-report/index.html
64+
retention-days: 30
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Test Vite Template
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'templates/vite/**'
8+
- 'tests/**'
9+
pull_request:
10+
branches: [main]
11+
paths:
12+
- 'templates/vite/**'
13+
- 'tests/**'
14+
15+
jobs:
16+
test-vite-template:
17+
uses: ./.github/workflows/test-template.yml
18+
with:
19+
template: vite

tests/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Dependencies
2+
node_modules/
3+
4+
# Playwright
5+
playwright-report/
6+
test-results/
7+
playwright/.cache/

tests/e2e/starter.spec.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('Starter Template', () => {
4+
test('should load the app and display main content', async ({ page }) => {
5+
await page.goto('/');
6+
7+
// Check that the page title is correct
8+
await expect(page).toHaveTitle('Power Apps');
9+
10+
// Check that the main heading is visible
11+
await expect(page.getByRole('heading', { name: 'Power + Code' })).toBeVisible();
12+
13+
// Check that the Power Apps and React logos are present
14+
await expect(page.getByAltText('Power Apps logo')).toBeVisible();
15+
await expect(page.getByAltText('React logo')).toBeVisible();
16+
});
17+
18+
test('should have working counter button', async ({ page }) => {
19+
await page.goto('/');
20+
21+
// Find the counter button and verify initial state
22+
const counterButton = page.getByRole('button', { name: /count is/i });
23+
await expect(counterButton).toBeVisible();
24+
await expect(counterButton).toHaveText('count is 0');
25+
26+
// Click the button and verify the count increments
27+
await counterButton.click();
28+
await expect(counterButton).toHaveText('count is 1');
29+
30+
// Click again to verify continued functionality
31+
await counterButton.click();
32+
await expect(counterButton).toHaveText('count is 2');
33+
});
34+
35+
test('should have theme toggle', async ({ page }) => {
36+
await page.goto('/');
37+
38+
// Check that the theme toggle button exists
39+
const themeToggle = page.getByRole('button', { name: /toggle theme/i });
40+
await expect(themeToggle).toBeVisible();
41+
});
42+
});

tests/e2e/vite.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('Vite React Template', () => {
4+
test('should load the app and display main content', async ({ page }) => {
5+
await page.goto('/');
6+
7+
// Check that the page title is correct
8+
await expect(page).toHaveTitle('vite');
9+
10+
// Check that the main heading is visible
11+
await expect(page.getByRole('heading', { name: 'Vite + React' })).toBeVisible();
12+
13+
// Check that the Vite and React logos are present
14+
await expect(page.getByAltText('Vite logo')).toBeVisible();
15+
await expect(page.getByAltText('React logo')).toBeVisible();
16+
});
17+
18+
test('should have working counter button', async ({ page }) => {
19+
await page.goto('/');
20+
21+
// Find the counter button and verify initial state
22+
const counterButton = page.getByRole('button', { name: /count is/i });
23+
await expect(counterButton).toBeVisible();
24+
await expect(counterButton).toHaveText('count is 0');
25+
26+
// Click the button and verify the count increments
27+
await counterButton.click();
28+
await expect(counterButton).toHaveText('count is 1');
29+
30+
// Click again to verify continued functionality
31+
await counterButton.click();
32+
await expect(counterButton).toHaveText('count is 2');
33+
});
34+
});

tests/package-lock.json

Lines changed: 90 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "powerappscodeapps-tests",
3+
"private": true,
4+
"version": "0.1.0",
5+
"scripts": {
6+
"test": "playwright test"
7+
},
8+
"devDependencies": {
9+
"@playwright/test": "^1.40.0",
10+
"@types/node": "^24.10.9"
11+
}
12+
}

tests/playwright.config.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { defineConfig, devices } from "@playwright/test";
2+
import path from "path";
3+
4+
const isCI = !!process.env.CI;
5+
const template: "vite" | "starter" = process.env.TEMPLATE; // Set to 'vite' or 'starter' to run only one
6+
7+
const webServers = {
8+
vite: {
9+
name: "vite-template",
10+
command: "npm run build && npm run preview -- --port 4173",
11+
cwd: path.resolve(__dirname, "../templates/vite"),
12+
url: "http://localhost:4173",
13+
reuseExistingServer: !isCI,
14+
},
15+
starter: {
16+
name: "starter-template",
17+
command: "npm run build && npm run preview -- --port 4174",
18+
cwd: path.resolve(__dirname, "../templates/starter"),
19+
url: "http://localhost:4174",
20+
reuseExistingServer: !isCI,
21+
},
22+
};
23+
24+
const projects = {
25+
vite: {
26+
name: "vite",
27+
testMatch: "vite.spec.ts",
28+
use: {
29+
...devices["Desktop Chrome"],
30+
baseURL: "http://localhost:4173",
31+
},
32+
},
33+
starter: {
34+
name: "starter",
35+
testMatch: "starter.spec.ts",
36+
use: {
37+
...devices["Desktop Chrome"],
38+
baseURL: "http://localhost:4174",
39+
},
40+
},
41+
};
42+
43+
export default defineConfig({
44+
testDir: "./e2e",
45+
fullyParallel: true,
46+
forbidOnly: isCI,
47+
retries: isCI ? 2 : 0,
48+
workers: isCI ? 1 : undefined,
49+
reporter: isCI ? [["github"], ["html", { open: "never" }]] : "html",
50+
use: {
51+
trace: "on-first-retry",
52+
},
53+
projects: projects[template] ? [projects[template]] : Object.values(projects),
54+
webServer: webServers[template] ?? Object.values(webServers),
55+
});

0 commit comments

Comments
 (0)