-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #126 from cisagov/add_playwright_ui_tests
Add Playwright to XFD
- Loading branch information
Showing
11 changed files
with
983 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
--- | ||
name: Playwright Tests | ||
on: | ||
deployment_status: | ||
paths: | ||
- playwright/** | ||
- .github/workflows/playwright.yml | ||
defaults: | ||
run: | ||
working-directory: ./playwright | ||
env: | ||
PW_XFD_2FA_ISSUER: ${{ secrets._PW_XFD_2FA_ISSUER }} | ||
PW_XFD_2FA_SECRET: ${{ secrets.PW_XFD_2FA_SECRET }} | ||
PW_XFD_PASSWORD: ${{ secrets.PW_XFD_PASSWORD }} | ||
PW_XFD_URL: ${{ vars.PW_XFD_URL }} | ||
PW_XFD_USER_ROLE: ${{ vars.PW_XFD_USER_ROLE }} | ||
PW_XFD_USERNAME: ${{ secrets.PW_XFD_USERNAME }} | ||
|
||
jobs: | ||
test: | ||
timeout-minutes: 60 | ||
runs-on: ubuntu-latest | ||
container: | ||
image: mcr.microsoft.com/playwright:v1.41.2-jammy | ||
if: github.event.deployment_status.state == 'success' | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: actions/setup-node@v3 | ||
with: | ||
node-version: 18 | ||
- name: Install dependencies | ||
run: npm ci | ||
- name: Run your tests | ||
run: npx playwright test | ||
env: | ||
HOME: /root |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/blob-report/ | ||
/playwright-report/ | ||
/playwright/.cache/ | ||
/test-results/ | ||
node_modules/ | ||
storageState.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { test, expect, Page } from '@playwright/test'; | ||
import exp from 'constants'; | ||
|
||
test.describe.configure({ mode: 'serial' }); | ||
let page: Page; | ||
|
||
test.beforeAll(async ({ browser }) => { | ||
page = await browser.newPage(); | ||
await page.goto('/'); | ||
}); | ||
|
||
test.afterAll(async () => { | ||
await page.close(); | ||
}); | ||
test('home', async () => { | ||
// Expect home page to show Latest Vulnerabilities. | ||
await expect( | ||
page.getByRole('heading', { name: 'Latest Vulnerabilities' }) | ||
).toBeVisible(); | ||
await expect( | ||
page.getByRole('heading', { name: 'Open Vulnerabilities by Domain' }) | ||
).toBeVisible(); | ||
await expect( | ||
page.getByRole('heading', { name: 'Most Common Ports' }) | ||
).toBeVisible(); | ||
await expect( | ||
page.getByRole('heading', { name: 'Most Common Vulnerabilities' }) | ||
).toBeVisible(); | ||
await expect( | ||
page.getByRole('heading', { name: 'Severity Levels' }) | ||
).toBeVisible(); | ||
await expect( | ||
page.getByPlaceholder('Search a domain, vuln, port, service, IP') | ||
).toBeVisible(); | ||
await expect(page.getByRole('link', { name: 'Inventory' })).toBeVisible(); | ||
await page.screenshot({ path: 'test-results/img/global-admin/home.png' }); | ||
}); | ||
|
||
test('Open Vulnerabilities by Domain', async () => { | ||
await page.getByRole('button', { name: 'All' }).click(); | ||
await page.screenshot({ | ||
path: 'test-results/img/global-admin/open_vuln_all.png' | ||
}); | ||
if ( | ||
(await page.getByRole('button', { name: 'Medium' }).isDisabled()) == false | ||
) { | ||
await page.getByRole('button', { name: 'Medium' }).click(); | ||
await page.screenshot({ | ||
path: 'test-results/img/global-admin/open_vuln_medium.png' | ||
}); | ||
} | ||
if ( | ||
(await page.getByRole('button', { name: 'High' }).isDisabled()) == false | ||
) { | ||
await page.getByRole('button', { name: 'High' }).click(); | ||
await page.screenshot({ | ||
path: 'test-results/img/global-admin/open_vuln_high.png' | ||
}); | ||
} | ||
if ((await page.getByLabel('Go to next page').isDisabled()) == false) { | ||
await page.getByLabel('Go to next page').click(); | ||
} | ||
if ((await page.getByLabel('Go to previous page').isDisabled()) == false) { | ||
await page.getByLabel('Go to previous page').click(); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { test, expect, chromium, Page } from '@playwright/test'; | ||
|
||
test.describe.configure({ mode: 'serial' }); | ||
let page: Page; | ||
|
||
test.beforeAll(async ({ browser }) => { | ||
page = await browser.newPage(); | ||
await page.goto('/'); | ||
}); | ||
|
||
test.afterAll(async () => { | ||
await page.close(); | ||
}); | ||
test('Inventory', async () => { | ||
await page.getByRole('link', { name: 'Inventory' }).click(); | ||
await expect(page).toHaveURL('/inventory'); | ||
await page.getByRole('button', { name: 'IP(s)' }).click(); | ||
await page.getByRole('button', { name: 'Severity' }).click(); | ||
await page.getByLabel('Sort by:').first().click(); | ||
await page.getByRole('option', { name: 'Domain Name' }).click(); | ||
await page.getByLabel('Sort by:').first().click(); | ||
await page.getByRole('option', { name: 'IP' }).click(); | ||
await page.getByLabel('Sort by:').first().click(); | ||
await page.getByRole('option', { name: 'Last Seen' }).click(); | ||
await page.getByLabel('Sort by:').first().click(); | ||
await page.getByRole('option', { name: 'First Seen' }).click(); | ||
await page.screenshot({ | ||
path: 'test-results/img/global-admin/inventory.png' | ||
}); | ||
}); | ||
|
||
test('Domains', async () => { | ||
await page.goto('/inventory'); | ||
await page.getByRole('link', { name: 'All Domains' }).click(); | ||
await expect(page).toHaveURL('/inventory/domains'); | ||
if ((await page.getByLabel('Go to next page').isDisabled()) == false) { | ||
await page.getByLabel('Go to next page').click(); | ||
} | ||
if ((await page.getByLabel('Go to previous page').isDisabled()) == false) { | ||
await page.getByLabel('Go to previous page').click(); | ||
} | ||
await page.screenshot({ path: 'test-results/img/global-admin/domains.png' }); | ||
}); | ||
|
||
test('Domain details', async () => { | ||
await page.goto('/inventory/domains'); | ||
await page.getByRole('row').nth(2).getByRole('link').click(); | ||
await expect(page).toHaveURL(new RegExp('/inventory/domain/')); | ||
await expect(page.getByText('IP:')).toBeVisible(); | ||
await expect(page.getByText('First Seen:')).toBeVisible(); | ||
await expect(page.getByText('Last Seen:')).toBeVisible(); | ||
await expect(page.getByText('Organization:')).toBeVisible(); | ||
await page.screenshot({ | ||
path: 'test-results/img/global-admin/domain_details.png' | ||
}); | ||
}); | ||
|
||
test('Domains filter', async () => { | ||
await page.goto('/inventory/domains'); | ||
await page.locator('#organizationName').click(); | ||
await page.locator('#organizationName').fill('Homeland'); | ||
await page.locator('#organizationName').press('Enter'); | ||
let rowCount = await page.getByRole('row').count(); | ||
for (let it = 2; it < rowCount; it++) { | ||
await expect( | ||
page.getByRole('row').nth(it).getByRole('cell').nth(1) | ||
).toContainText('Homeland'); | ||
} | ||
await page.screenshot({ | ||
path: 'test-results/img/global-admin/domain_filter.png' | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { test, expect, chromium, Page } from '@playwright/test'; | ||
|
||
test.describe.configure({ mode: 'serial' }); | ||
let page: Page; | ||
|
||
test.beforeAll(async ({ browser }) => { | ||
page = await browser.newPage(); | ||
await page.goto('/'); | ||
}); | ||
|
||
test.afterAll(async () => { | ||
await page.close(); | ||
}); | ||
|
||
test('Vulnerabilities', async () => { | ||
await page.getByRole('link', { name: 'Inventory' }).click(); | ||
await page.getByRole('link', { name: 'All Vulnerabilities' }).click(); | ||
await expect(page).toHaveURL('/inventory/vulnerabilities'); | ||
if ((await page.getByLabel('Go to next page').isDisabled()) == false) { | ||
await page.getByLabel('Go to next page').click(); | ||
} | ||
if ((await page.getByLabel('Go to previous page').isDisabled()) == false) { | ||
await page.getByLabel('Go to previous page').click(); | ||
} | ||
await page.screenshot({ | ||
path: 'test-results/img/global-admin/vulnerabilities.png' | ||
}); | ||
}); | ||
|
||
test('Vulnerability details NIST', async () => { | ||
await page.goto('/inventory/vulnerabilities'); | ||
const newTabPromise = page.waitForEvent('popup'); | ||
await page.getByRole('row').nth(2).getByRole('link').nth(0).click(); | ||
const newTab = await newTabPromise; | ||
await newTab.waitForLoadState(); | ||
await expect(newTab).toHaveURL( | ||
new RegExp('^https://nvd\\.nist\\.gov/vuln/detail/') | ||
); | ||
}); | ||
|
||
test('Domain details link', async () => { | ||
await page.goto('/inventory/vulnerabilities'); | ||
await page.getByRole('row').nth(2).getByRole('link').nth(1).click(); | ||
await expect(page).toHaveURL(new RegExp('/inventory/domain/')); | ||
}); | ||
|
||
test('Vulnerability details', async () => { | ||
await page.goto('/inventory/vulnerabilities'); | ||
await page.getByRole('row').nth(2).getByRole('link').nth(2).click(); | ||
await expect(page).toHaveURL(new RegExp('/inventory/vulnerability/')); | ||
await expect(page.getByRole('heading', { name: 'Overview' })).toBeVisible(); | ||
await expect( | ||
page.getByRole('heading', { name: 'Installed (Known) Products' }) | ||
).toBeVisible(); | ||
await expect(page.getByRole('heading', { name: 'Provenance' })).toBeVisible(); | ||
await expect( | ||
page.getByRole('heading', { name: 'Vulnerability Detection History' }) | ||
).toBeVisible(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { chromium, FullConfig, test as setup } from '@playwright/test'; | ||
import * as OTPAuth from 'otpauth'; | ||
import * as dotenv from 'dotenv'; | ||
|
||
dotenv.config(); | ||
|
||
const authFile = './storageState.json'; | ||
|
||
let totp = new OTPAuth.TOTP({ | ||
issuer: process.env.PW_XFD_2FA_ISSUER, | ||
label: 'Crossfeed', | ||
algorithm: 'SHA1', | ||
digits: 6, | ||
period: 30, | ||
secret: process.env.PW_XFD_2FA_SECRET | ||
}); | ||
|
||
async function globalSetup(config: FullConfig) { | ||
const { baseURL, storageState } = config.projects[0].use; | ||
const browser = await chromium.launch(); | ||
const page = await browser.newPage(); | ||
|
||
//Log in with credentials. | ||
await page.goto(String(process.env.PW_XFD_URL)); | ||
await page | ||
.getByPlaceholder('Enter your email address') | ||
.fill(String(process.env.PW_XFD_USERNAME)); | ||
await page | ||
.getByPlaceholder('Enter your password') | ||
.fill(String(process.env.PW_XFD_PASSWORD)); | ||
await page.getByRole('button', { name: 'Sign in' }).click(); | ||
await page | ||
.getByPlaceholder('Enter code from your authenticator app') | ||
.fill(totp.generate()); | ||
await page.getByRole('button', { name: 'Confirm' }).click(); | ||
//Wait for storageState to write to json file for other tests to use. | ||
await page.waitForTimeout(1000); | ||
await page.context().storageState({ path: authFile }); | ||
await page.close(); | ||
} | ||
|
||
export default globalSetup; |
Oops, something went wrong.