Skip to content

Commit

Permalink
Merge pull request #126 from cisagov/add_playwright_ui_tests
Browse files Browse the repository at this point in the history
Add Playwright to XFD
  • Loading branch information
JCantu248 authored Apr 9, 2024
2 parents 8ff7790 + 8b33dd3 commit e8ea2f1
Show file tree
Hide file tree
Showing 11 changed files with 983 additions and 0 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/playwright.yml
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
6 changes: 6 additions & 0 deletions playwright/.gitignore
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
66 changes: 66 additions & 0 deletions playwright/e2e/global-admin/home.spec.ts
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();
}
});
72 changes: 72 additions & 0 deletions playwright/e2e/global-admin/inventory.spec.ts
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'
});
});
59 changes: 59 additions & 0 deletions playwright/e2e/global-admin/vulnerabilities.spec.ts
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();
});
42 changes: 42 additions & 0 deletions playwright/global-setup.ts
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;
Loading

0 comments on commit e8ea2f1

Please sign in to comment.