add playwright e2e tests

pull/154/head
Marcus Felling 2022-09-16 11:18:25 -05:00
parent 26342db572
commit 078fd0eaaf
No known key found for this signature in database
GPG Key ID: 89EBCF55B58D842F
12 changed files with 331 additions and 5 deletions

View File

@ -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

5
.gitignore vendored
View File

@ -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/

View File

@ -13,6 +13,7 @@ products:
- azure-container-apps
- azure-container-registry
- azure-app-service-web
- playwright
---
# .NET Podcasts - Sample Application

74
src/Web/E2E/package-lock.json generated 100644
View File

@ -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
}
}
}

View File

@ -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"
}
}

View File

@ -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;

View File

@ -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');
});
});

View File

@ -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');
});
});

View File

@ -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();
});
});

View File

@ -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')
});
});

View File

@ -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();
}
});
});

View File

@ -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 havent subscribed to any channel yet.');
});
});