add playwright e2e tests
parent
26342db572
commit
078fd0eaaf
|
@ -20,7 +20,7 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
environment:
|
||||
environment:
|
||||
name: prod
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
@ -53,10 +53,10 @@ jobs:
|
|||
- name: Set Blazor WASM app settings
|
||||
uses: microsoft/variable-substitution@v1
|
||||
with:
|
||||
files: 'src/Web/Client/wwwroot/appsettings.json'
|
||||
files: "src/Web/Client/wwwroot/appsettings.json"
|
||||
env:
|
||||
PodcastApi.BaseAddress: ${{ env.PODCAST_API_URL }}
|
||||
ListenTogetherHub: ${{ env.LISTEN_TOGETHER_HUB_URL }}
|
||||
ListenTogetherHub: ${{ env.LISTEN_TOGETHER_HUB_URL }}
|
||||
|
||||
- name: Build
|
||||
run: dotnet build src/Web/Server --configuration Release
|
||||
|
@ -75,7 +75,7 @@ jobs:
|
|||
|
||||
deploy:
|
||||
needs: build
|
||||
environment:
|
||||
environment:
|
||||
name: prod
|
||||
if: ${{ github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -98,7 +98,7 @@ jobs:
|
|||
with:
|
||||
name: drop
|
||||
path: web
|
||||
|
||||
|
||||
- name: Update App Service app settings variables
|
||||
uses: Azure/appservice-settings@v1
|
||||
with:
|
||||
|
@ -120,3 +120,40 @@ jobs:
|
|||
with:
|
||||
app-name: ${{ secrets.WEBAPP_NAME }}
|
||||
package: web
|
||||
|
||||
test:
|
||||
needs: deploy
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
container: mcr.microsoft.com/playwright:v1.25.2-focal
|
||||
env:
|
||||
BASEURL: https://${{secrets.WEBAPP_NAME}}.azurewebsites.net # sets value for URL to test
|
||||
defaults:
|
||||
run:
|
||||
working-directory: src/Web/E2E
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14.x"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: |
|
||||
HOME=/root npx playwright test
|
||||
|
||||
- name: Create test summary
|
||||
uses: test-summary/action@dist
|
||||
if: always()
|
||||
with:
|
||||
paths: src/Web/E2E/test-results/junit.xml
|
||||
|
||||
- name: Upload HTML report
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: src/Web/E2E/playwright-report/
|
||||
retention-days: 30
|
||||
|
|
|
@ -349,3 +349,8 @@ MigrationBackup/
|
|||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
*.DS_Store
|
||||
|
||||
# Playwright tests
|
||||
src/Web/E2E/test-results/
|
||||
src/Web/E2E/playwright-report/
|
||||
src/Web/E2E/playwright/.cache/
|
||||
|
|
|
@ -13,6 +13,7 @@ products:
|
|||
- azure-container-apps
|
||||
- azure-container-registry
|
||||
- azure-app-service-web
|
||||
- playwright
|
||||
---
|
||||
|
||||
# .NET Podcasts - Sample Application
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"name": "dotnet-podcasts",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "dotnet-podcasts",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.25.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.25.2.tgz",
|
||||
"integrity": "sha512-6qPznIR4Fw02OMbqXUPMG6bFFg1hDVNEdihKy0t9K0dmRbus1DyP5Q5XFQhGwEHQkLG5hrSfBuu9CW/foqhQHQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.25.2"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.7.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.16.tgz",
|
||||
"integrity": "sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.25.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.25.2.tgz",
|
||||
"integrity": "sha512-0yTbUE9lIddkEpLHL3u8PoCL+pWiZtj5A/j3U7YoNjcmKKDGBnCrgHJMzwd2J5vy6l28q4ki3JIuz7McLHhl1A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/test": {
|
||||
"version": "1.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.25.2.tgz",
|
||||
"integrity": "sha512-6qPznIR4Fw02OMbqXUPMG6bFFg1hDVNEdihKy0t9K0dmRbus1DyP5Q5XFQhGwEHQkLG5hrSfBuu9CW/foqhQHQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.25.2"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.7.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.16.tgz",
|
||||
"integrity": "sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg==",
|
||||
"dev": true
|
||||
},
|
||||
"playwright-core": {
|
||||
"version": "1.25.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.25.2.tgz",
|
||||
"integrity": "sha512-0yTbUE9lIddkEpLHL3u8PoCL+pWiZtj5A/j3U7YoNjcmKKDGBnCrgHJMzwd2J5vy6l28q4ki3JIuz7McLHhl1A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "dotnet-podcasts",
|
||||
"version": "1.0.0",
|
||||
"description": "---\r page_type: sample\r description: \".NET 6 reference application shown at .NET Conf 2021 featuring ASP.NET Core, Blazor, .NET MAUI, Microservices, and more!\"\r languages:\r - csharp\r products:\r - dotnet-core\r - ef-core\r - blazor\r - dotnet-maui\r - azure-sql-database\r - azure-storage\r - azure-container-apps\r - azure-container-registry\r - azure-app-service-web\r ---",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"doc": "docs"
|
||||
},
|
||||
"scripts": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Microsoft/dotnet-podcasts.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Microsoft/dotnet-podcasts/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Microsoft/dotnet-podcasts#readme",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.25.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
import { devices } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: './tests',
|
||||
/* 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: 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: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: [
|
||||
['html'],
|
||||
['junit', { outputFile: './test-results/junit.xml' }],
|
||||
],
|
||||
/* 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: process.env.BASEURL,
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on',
|
||||
video: 'on',
|
||||
},
|
||||
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'firefox',
|
||||
use: {
|
||||
...devices['Desktop Firefox'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'webkit',
|
||||
use: {
|
||||
...devices['Desktop Safari'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -0,0 +1,31 @@
|
|||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
test.describe.configure({ mode: 'parallel' });
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/discover');
|
||||
});
|
||||
|
||||
test.describe('Discover', () => {
|
||||
test('should allow me to browse categories', async ({ page }) => {
|
||||
// Loop through each category
|
||||
for (const category of ['Microsoft', 'Mobile', 'Community', 'M365']) {
|
||||
// click on the category
|
||||
await page.locator('.tags-item >> text=' + category).click();
|
||||
// assert category is selected
|
||||
await expect(page.locator('.titlePage')).toHaveText(category);
|
||||
// navigate back to discover page
|
||||
await page.locator('button:has-text("Back")').click();
|
||||
}
|
||||
});
|
||||
|
||||
test('should allow me to search', async ({ page }) => {
|
||||
// use search bar
|
||||
await page.locator('[placeholder="Search here"]').click();
|
||||
// search for a podcast
|
||||
await page.locator('[placeholder="Search here"]').fill('.NET');
|
||||
await page.locator('[placeholder="Search here"]').press('Enter');
|
||||
// assert no results page isn't shown
|
||||
expect(page.locator('.main')).not.toContain('no results');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/discover');
|
||||
});
|
||||
|
||||
test.describe('Listen Later', () => {
|
||||
test('should allow me to listen to podcast later', async ({ page }) => {
|
||||
// click first podcast in list
|
||||
await page.locator('.item-primary-action').first().click();
|
||||
// click first listen later button
|
||||
await page.locator('button.buttonIcon.episode-actions-later').first().click();
|
||||
// view listen later tab
|
||||
await page.locator('.navbarApp-item >> text=ListenLater').click();
|
||||
await expect(page).toHaveURL('/listen-later');
|
||||
// assert no results page isn't shown
|
||||
expect(page.locator('.main')).not.toContain('no results');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/discover');
|
||||
});
|
||||
|
||||
test.describe('Listen Together', () => {
|
||||
test('should allow me to listen together', async ({ page }) => {
|
||||
// click first podcast in list
|
||||
await page.locator('.item-primary-action').first().click();
|
||||
// click play
|
||||
await page.locator('.icon-play').first().click();
|
||||
// click go to listen together page
|
||||
await page.locator('text=ListenTogether').click();
|
||||
await expect(page).toHaveURL('/listen-together');
|
||||
// assert Create new room button isn't disabled
|
||||
expect(page.locator('.buttonApp.primary >> text=Create new room')).toBeEnabled
|
||||
// create new room
|
||||
await page.locator('.buttonApp.primary >> text=Create new room').click();
|
||||
await page.locator('[placeholder="Your name"]').fill('test');
|
||||
// open room
|
||||
await page.locator('button:has-text("Open room")').click();
|
||||
// leave the room
|
||||
await page.locator('text=Leave the room').click();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('');
|
||||
});
|
||||
|
||||
test.describe('Login', () => {
|
||||
test('should allow me to login', async ({ page }) => {
|
||||
// click sign in
|
||||
await page.locator('text=Sign In').click();
|
||||
// assert discover page is shown
|
||||
await expect(page).toHaveURL('/discover');
|
||||
expect(page).toHaveTitle('.NET Podcasts')
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/settings');
|
||||
});
|
||||
|
||||
test.describe('Settings', () => {
|
||||
test('should allow me to toggle settings', async ({ page }) => {
|
||||
// loop through each setting
|
||||
for (const setting of ['autodownload', 'deleteplayed', 'systemtheme', 'darktheme']) {
|
||||
// toggle setting
|
||||
await page.locator('input[name="' + setting + '"]').check();
|
||||
await page.locator('input[name="' + setting + '"]').uncheck();
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/discover');
|
||||
});
|
||||
|
||||
test.describe('Subscriptions', () => {
|
||||
test('should allow me to subscribe', async ({ page }) => {
|
||||
// click first podcast in list
|
||||
await page.locator('.item-primary-action').first().click();
|
||||
// click subscribe
|
||||
await page.locator('button:has-text("Subscribe")').click();
|
||||
// view subscriptions
|
||||
await page.locator('.navbarApp-item >> text=subscriptions').click();
|
||||
await expect(page).toHaveURL('/subscriptions');
|
||||
// assert subscriptions are shown
|
||||
expect(page.locator('.main')).not.toContain('You haven’t subscribed to any channel yet.');
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue