Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2E tests with Playwright #666

Merged
merged 5 commits into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .github/workflows/lintBuildTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ name: CI

on:
push:
branches:
- main
branches: [main]
pull_request:

jobs:
ci:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- uses: actions/setup-node@v2
with:
node-version: '16'
Expand All @@ -35,3 +32,18 @@ jobs:

- name: Build for Nexus
run: yarn build-for-nexus

playwright:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '16.x'
- name: Install dependencies
run: yarn
- name: Install Playwright
run: npx playwright install --with-deps
- name: Run Playwright tests
run: yarn playwright test
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@
# System Files
.DS_Store
Thumbs.db
test-results/
playwright-report/
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ Using the script is strongly recommended, but if you really don't want to, make

</details>

### E2E tests with [Playwright](https://playwright.dev/)

The basic command to run the tests is `yarn playwright test`. Some debugging tricks (more in the docs [here](https://playwright.dev/docs/debug)):

- Add `--headed` to run the full tests in visible browsers (but they go by too fast to really see anything)
- (Requires `--headed`) The simplest way to debug at a particular spot is to add `await page.pause();` right before it. This runs the test in a headed browser with the excellent [Inspector](https://playwright.dev/docs/inspector) and pauses the debugger. This is perfect for making sure the screen looks like you expect at that moment and testing selectors to use in the next step.
- `PWDEBUG=1 yarn playwright test` starts the tests in headed browser with the inspector, paused on the first line of the first test
- Add `--project=chromium` to only run the tests in a single browser
- Useful in combination with the inspector options because 3 browsers and 3 inspectors is not very useful (and it doesn't say which inspector goes with which browser)

### Other useful commands

| Command | Description |
Expand Down
196 changes: 196 additions & 0 deletions app/e2e/VpcPage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import { test, expect } from '@playwright/test'

test.describe('VpcPage', () => {
test('can nav to VpcPage from /', async ({ page }) => {
await page.goto('/')
await page.click('table :text("mock-project")')
await page.click('a:has-text("Networking")')
await page.click('a:has-text("mock-vpc")')
await expect(page.locator('text=mock-subnet')).toBeVisible()
})

test('can create subnet', async ({ page }) => {
await page.goto('/orgs/maze-war/projects/mock-project/vpcs/mock-vpc')
// only one row in table, the default mock-subnet
const rows = await page.locator('tbody >> tr')
await expect(rows).toHaveCount(1)
await expect(rows.nth(0).locator('text="mock-subnet"')).toBeVisible()

// open modal, fill out form, submit
await page.click('text=New subnet')
await page.fill('text=IPv4 block', '1.1.1.2/24')
await page.fill('input[name=name]', 'mock-subnet-2')
await page.click('button:has-text("Create subnet")')

await expect(rows).toHaveCount(2)

await expect(rows.nth(0).locator('text="mock-subnet"')).toBeVisible()
await expect(rows.nth(0).locator('text="1.1.1.1/24"')).toBeVisible()

await expect(rows.nth(1).locator('text="mock-subnet-2"')).toBeVisible()
await expect(rows.nth(1).locator('text="1.1.1.2/24"')).toBeVisible()
})

const defaultRules = [
'allow-internal-inbound',
'allow-ssh',
'allow-icmp',
'allow-rdp',
]

test('can create firewall rule', async ({ page }) => {
await page.goto('/orgs/maze-war/projects/mock-project/vpcs/mock-vpc')
await page.locator('text="Firewall Rules"').click()

// default rules are all there
for (const name of defaultRules) {
await expect(page.locator(`text="${name}"`)).toBeVisible()
}
const rows = page.locator('tbody >> tr')
await expect(rows).toHaveCount(4)

const modal = page.locator('text="Create firewall rule"')
await expect(modal).not.toBeVisible()

// open modal
await page.locator('text="New rule"').click()

// modal is now open
await expect(modal).toBeVisible()

await page.fill('input[name=name]', 'my-new-rule')
await page.locator('text=Outgoing').click()

await page.fill('text="Priority"', '5')

// add target VPC "my-target-vpc"
await page.locator('button:has-text("Target type")').click()
await page.locator('[role=option] >> text="VPC"').click()
await page.fill('text="Target name"', 'my-target-vpc')
await page.locator('text="Add target"').click()

// target is added to targets table
await expect(page.locator('td:has-text("my-target-vpc")')).toBeVisible()

// add host filter instance "host-filter-instance"
await page.locator('button:has-text("Host type")').click()
await page.locator('[role=option] >> text="Instance"').click()
await page.fill('text="Value"', 'host-filter-instance')
await page.locator('text="Add host filter"').click()

// host is added to hosts table
await expect(
page.locator('td:has-text("host-filter-instance")')
).toBeVisible()

// TODO: test invalid port range once I put an error message in there
await page.fill('text="Port filter"', '123-456')
await page.locator('text="Add port filter"').click()

// port range is added to port ranges table
await expect(page.locator('td:has-text("123-456")')).toBeVisible()

// check the UDP box
await page.locator('text=UDP').click()

// submit the form
await page.locator('text="Create rule"').click()

// modal closes again
await expect(modal).not.toBeVisible()

// table refetches and now includes the new rule as well as the originals
await expect(page.locator('td >> text="my-new-rule"')).toBeVisible()
// target shows up in target cell
await expect(page.locator('text=vpcmy-target-vpc')).toBeVisible()
// other stuff filled out shows up in the filters column
await expect(
page.locator('text=instancehost-filter-instanceUDP123-456')
).toBeVisible()

await expect(rows).toHaveCount(5)
for (const name of defaultRules) {
await expect(page.locator(`text="${name}"`)).toBeVisible()
}
})

test('can update firewall rule', async ({ page }) => {
await page.goto('/orgs/maze-war/projects/mock-project/vpcs/mock-vpc')
await page.locator('text="Firewall Rules"').click()

const rows = await page.locator('tbody >> tr')
await expect(rows).toHaveCount(4)

// allow-icmp is the one we're doing to change
const oldNameCell = page.locator('td >> text="allow-icmp"')
expect(oldNameCell).toBeVisible()

const newNameCell = page.locator('td >> text="new-rule-name"')
expect(newNameCell).not.toBeVisible()

const modal = page.locator('text="Edit firewall rule"')
await expect(modal).not.toBeVisible()

// click more button on allow-icmp row to get menu, then click Edit
page.locator('tr:has-text("allow-icmp") button:has-text("More")').click()
// filter visible to distinguish from all the hidden menus' Edit button
await page.locator('text="Edit" >> visible=true').click()

// modal is now open
await expect(modal).toBeVisible()

// TODO: get these by their label when that becomes easier to do

// name is populated
await expect(page.locator('input[name=name]')).toHaveValue('allow-icmp')

// priority is populated
await expect(page.locator('input[name=priority]')).toHaveValue('65534')

// protocol is populated
await expect(page.locator('label >> text=ICMP')).toBeChecked()
await expect(page.locator('label >> text=TCP')).not.toBeChecked()
await expect(page.locator('label >> text=UDP')).not.toBeChecked()

// targets default vpc
// screen.getByRole('cell', { name: 'vpc' })
// screen.getByRole('cell', { name: 'default' })

// update name
await page.fill('input[name=name]', 'new-rule-name')

// add host filter
await page.locator('button:has-text("Host type")').click()
await page.locator('[role=option] >> text="Instance"').click()
await page.fill('text="Value"', 'edit-filter-instance')
await page.locator('text="Add host filter"').click()

// new host is added to hosts table
await expect(
page.locator('td:has-text("edit-filter-instance")')
).toBeVisible()

// submit the form
await page.locator('text="Update rule"').click()

// modal closes again
await expect(modal).not.toBeVisible()

// table refetches and now includes the updated rule name, not the old name
await expect(newNameCell).toBeVisible()
expect(oldNameCell).not.toBeVisible()

await expect(rows).toHaveCount(4)

// new target shows up in target cell
await expect(
page.locator('text=instanceedit-filter-instanceICMP')
).toBeVisible()

// other 3 rules are still there
const rest = defaultRules.filter((r) => r !== 'allow-icmp')
for (const name of rest) {
await expect(page.locator(`text="${name}"`)).toBeVisible()
}
})
})
Loading