Skip to content

Commit

Permalink
Add playwright for frontend specs (#560)
Browse files Browse the repository at this point in the history
  • Loading branch information
spuun authored Nov 10, 2023
1 parent f0d5741 commit 1305a87
Show file tree
Hide file tree
Showing 27 changed files with 561 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ on:
- Makefile
- 'src/**'
- 'spec/**'
- 'playwright.config.js'
- 'shard.*'
- 'static/**'
- 'views/**'
Expand Down Expand Up @@ -134,6 +135,60 @@ jobs:
name: bin
path: bin/

path_filter:
name: Filters for fronted specs
runs-on: ubuntu-latest
outputs:
views: ${{ steps.changes.outputs.views }}
js: ${{ steps.changes.outputs.js }}
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: dorny/paths-filter@v2
id: changes
with:
filters: |
views:
- '**/*.ecr'
js:
- '**/*.js'
spec_frontend:
name: Fronted specs
runs-on: ubuntu-latest
needs: [compile, path_filter]
if: ${{ needs.path_filter.outputs.views == 'true' || needs.path_filter.outputs.js == 'true' }}
steps:
- name: Install LavinMQ dependencies
run: |
sudo apt-get update
sudo apt-get install -y make libevent-2.1-7
- name: Download lavinmq
uses: actions/download-artifact@v3
with:
name: bin
path: bin

- name: Run LavinMQ in background
run: |
chmod +x bin/*
bin/lavinmq --data-dir=/tmp/amqp --bind=:: &
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20

- name: Install @playwright/test
run: npm install @playwright/test

- name: Install browsers
run: npx playwright install --with-deps

- name: Run tests
run: npx playwright test --reporter=list

java-client-test:
name: RabbitMQ java client test
runs-on: ubuntu-20.04
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ config.ini
*.pem
perf.data*
massif.*
node_modules
package.json
package-lock.json
/playwright/
63 changes: 63 additions & 0 deletions playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// @ts-check
import { defineConfig, devices, expect } from '@playwright/test'
import './spec/frontend/expect_extensions.js'

/**
* @see https://playwright.dev/docs/test-configuration
*/
module.exports = defineConfig(
{
testDir: './spec/frontend',
/* 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: process.env.BASE_URL ?? 'http://127.0.0.1:15672',

/* 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: 'setup',
testMatch: /.*\.setup\.js/
},
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'],
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'],
},

{
name: 'webkit',
use: {
...devices['Desktop Safari'],
storageState: 'playwright/.auth/user.json'
},
dependencies: ['setup'],
},
],
})

3 changes: 3 additions & 0 deletions spec/frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Frontend specs

These specs are some frontend.
14 changes: 14 additions & 0 deletions spec/frontend/auth.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { test, test as setup, expect } from '@playwright/test';

const authFile = 'playwright/.auth/user.json';


setup('authenticate', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Username').fill('guest');
await page.getByLabel('Password').fill('guest');
await page.getByRole('button').click();
await page.waitForURL('/');
await page.context().storageState({ path: authFile });
});

11 changes: 11 additions & 0 deletions spec/frontend/auth.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @ts-check
import { test, expect } from '@playwright/test';

test.describe('when unauthenticated', _ => {
test.use({ storageState: {} })
test('redirects to login', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveURL(/\/login$/);
})
})

11 changes: 11 additions & 0 deletions spec/frontend/channel.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("channel", _ => {
test('is loaded', async ({ page, baseURL }) => {
const channelName = "127.0.0.1:63221[1]"
const apiChannelRequest = helpers.waitForPathRequest(page, `/api/channels/${channelName}`, {})
await page.goto(`/channel#name=${channelName}`)
await expect(apiChannelRequest).toBeRequested()
})
})
10 changes: 10 additions & 0 deletions spec/frontend/channels.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("channels", _ => {
test('are loaded', async ({ page, baseURL }) => {
const apiChannelsRequest = helpers.waitForPathRequest(page, '/api/channels', {})
await page.goto('/channels')
await expect(apiChannelsRequest).toBeRequested()
})
})
17 changes: 17 additions & 0 deletions spec/frontend/connection.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("connection", _ => {
test('info is loaded', async ({ page, baseURL }) => {
const connectionName = "127.0.0.1:63610 -> 127.0.0.1:12345"
const apiConnectionRequest = helpers.waitForPathRequest(page, `/api/connections/${connectionName}`, {})
await page.goto(`/connection#name=${connectionName}`)
await expect(apiConnectionRequest).toBeRequested()
})
test('channels are loaded', async ({ page, baseURL }) => {
const connectionName = "127.0.0.1:63610 -> 127.0.0.1:12345"
const apiChannelsRequest = helpers.waitForPathRequest(page, `/api/connections/${connectionName}/channels`, {})
await page.goto(`/connection#name=${connectionName}`)
await expect(apiChannelsRequest).toBeRequested()
})
})
10 changes: 10 additions & 0 deletions spec/frontend/connections.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("connections", _ => {
test('are loaded', async ({ page, baseURL }) => {
const apiConnectionsRequest = helpers.waitForPathRequest(page, '/api/connections')
await page.goto('/connections')
await expect(apiConnectionsRequest).toBeRequested()
})
})
10 changes: 10 additions & 0 deletions spec/frontend/exchanges.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("exchanges", _ => {
test('are loaded', async ({ page, baseURL }) => {
const apiExchangesRequest = helpers.waitForPathRequest(page, '/api/exchanges', {})
await page.goto('/exchanges')
await expect(apiExchangesRequest).toBeRequested()
})
})
47 changes: 47 additions & 0 deletions spec/frontend/expect_extensions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { expect } from '@playwright/test';

expect.extend(
{
async toBeRequested(received, params) {
return received.then(reqs => {
return {
message: _ => 'requested',
pass: true
}
}).catch(e => {
return {
message: _ => e.message,
pass: false
}
})
},

async toHaveQueryParams(received, query) {
try {
const request = await received
const requestedUrl = new URL(request.url())
const expectedParams = new URLSearchParams(query)
const actualParams = requestedUrl.searchParams
for (let [name, value] of expectedParams.entries()) {
const actualValue = actualParams.get(name)
if (actualValue !== value) {
return {
message: _ => `expected query param '${name}' to be '${value}', got '${actualValue}'`,
pass: false
}
}
}
return {
message: _ => "yes",
pass: true
}
} catch(e) {
return {
message: _ => e.toString(),
pass: false
}
}
}
})


12 changes: 12 additions & 0 deletions spec/frontend/federation.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("federation", _ => {
test('are loaded', async ({ page, baseURL }) => {
const apiFederationUpstreamsRequest = helpers.waitForPathRequest(page, '/api/parameters/federation-upstream', {})
const apiFederationLinksRequest = helpers.waitForPathRequest(page, '/api/federation-links', {})
await page.goto('/federation')
await expect(apiFederationUpstreamsRequest).toBeRequested()
await expect(apiFederationLinksRequest).toBeRequested()
})
})
14 changes: 14 additions & 0 deletions spec/frontend/fixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { test as base, expect } from "@playwright/test";

const vhosts = ['foo', 'bar']
const test = base.extend(
{
vhosts,
page: async ({ baseURL, page }, use) => {
const vhostResponse = vhosts.map(x => { return {name: x} })
await page.route(/\/api\/vhosts(\?|$)/, async route => await route.fulfill({ json: vhostResponse }))
use(page)
}
})

export { test, expect }
22 changes: 22 additions & 0 deletions spec/frontend/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
function waitForPathRequest(page, path, response_data = {}) {
const matchUrl = new URL(path, 'http://example.com')
return new Promise((resolve, reject) => {
const handler = (route, request) => {
const requestedUrl = new URL(request.url())
if (decodeURIComponent(requestedUrl.pathname) !== decodeURIComponent(matchUrl.pathname)) {
return route.continue()
}
page.unroute('**/*', handler)
route.fulfill({ json: response_data })
resolve(request)
}
page.route('**/*', handler)
})
return page.waitForRequest(req => {
const reqUrl = new URL(req.url())
return reqUrl.pathname == path
}, { timeout: 1000 })
}


export { waitForPathRequest }
17 changes: 17 additions & 0 deletions spec/frontend/layout.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { test, expect } from './fixtures.js'

test.describe("vhosts", _ => {
test('are loaded', async ({ page, baseURL, vhosts }) => {
await page.goto('/')
const vhostOptions = await page.locator('#userMenuVhost option').allTextContents()
vhosts.forEach(vhost => {
expect(vhostOptions).toContain(vhost)
})
})

test('remember selection', async ({ page }) => {
await page.goto('/')
await page.locator('#userMenuVhost').selectOption('foo') // selectOption trigger page load
await expect(page.locator('#userMenuVhost option:checked')).toHaveText(['foo'])
})
})
10 changes: 10 additions & 0 deletions spec/frontend/logs.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("logs", _ => {
test('are loaded', async ({ page, baseURL }) => {
const apiLogsRequest = page.waitForRequest(/\/api\/livelog$/, { timeout: 1000 })
await page.goto('/logs')
await expect(apiLogsRequest).toBeRequested()
})
})
10 changes: 10 additions & 0 deletions spec/frontend/nodes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("nodes", _ => {
test('are loaded', async ({ page, baseURL }) => {
const apiNodesRequest = helpers.waitForPathRequest(page, '/api/nodes', {})
await page.goto('/nodes')
await expect(apiNodesRequest).toBeRequested()
})
})
10 changes: 10 additions & 0 deletions spec/frontend/operator-policies.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("operator-policies", _ => {
test('are loaded', async ({ page, baseURL }) => {
const apiPoliciesRequest = helpers.waitForPathRequest(page, '/api/operator-policies', {})
await page.goto('/operator-policies')
await expect(apiPoliciesRequest).toBeRequested()
})
})
10 changes: 10 additions & 0 deletions spec/frontend/overview.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as helpers from './helpers.js'
import { test, expect } from './fixtures.js';

test.describe("overview", _ => {
test('are loaded', async ({ page, baseURL }) => {
const apiOverviewRequest = helpers.waitForPathRequest(page, '/api/overview')
await page.goto('/')
await expect(apiOverviewRequest).toBeRequested()
})
})
Loading

0 comments on commit 1305a87

Please sign in to comment.