Skip to content

Commit ca1bbc8

Browse files
authored
chore: add home page automation tests (#370)
1 parent 1e6afa4 commit ca1bbc8

File tree

7 files changed

+140
-25
lines changed

7 files changed

+140
-25
lines changed

.env.example

+3
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
VITE_PARSEABLE_URL="https://demo.parseable.com"
2+
VITE_USE_BASIC_AUTH=true
3+
VITE_USERNAME=admin
4+
VITE_PASSWORD=admin

.github/workflows/playwright.yml

-5
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ jobs:
1717
run: npm install -g pnpm && pnpm install
1818
- name: Install Playwright Browsers
1919
run: pnpm exec playwright install --with-deps
20-
- name: Start the development server
21-
run: pnpm run dev &
22-
env:
23-
PORT: 3001
24-
VITE_PARSEABLE_URL: 'https://demo.parseable.com'
2520
- name: Run Playwright tests
2621
run: pnpm exec playwright test
2722
- uses: actions/upload-artifact@v4

playwright.config.ts

+23-17
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ import { defineConfig, devices } from '@playwright/test';
22

33
export default defineConfig({
44
testDir: './tests',
5+
testIgnore: '**/login.spec.ts',
56
/* Run tests in files in parallel */
6-
fullyParallel: true,
7+
fullyParallel: true, // Set this to false to ensure sequential execution of files
78
/* Fail the build on CI if you accidentally left test.only in the source code. */
89
forbidOnly: !!process.env.CI,
910
/* Retry on CI only */
1011
retries: process.env.CI ? 2 : 0,
1112
/* Opt out of parallel tests on CI. */
1213
workers: process.env.CI ? 1 : undefined,
1314
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
14-
reporter: 'html',
15+
reporter: 'line',
1516
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
1617
use: {
1718
/* Base URL to use in actions like `await page.goto('/')`. */
@@ -27,22 +28,27 @@ export default defineConfig({
2728
name: 'chromium',
2829
use: { ...devices['Desktop Chrome'] },
2930
},
30-
31-
{
32-
name: 'firefox',
33-
use: { ...devices['Desktop Firefox'] },
34-
},
35-
36-
{
37-
name: 'webkit',
38-
use: { ...devices['Desktop Safari'] },
39-
},
31+
// {
32+
// name: 'Firefox',
33+
// use: { ...devices['Desktop Firefox'] },
34+
// },
35+
// {
36+
// name: 'Safari',
37+
// use: { ...devices['Desktop Safari'] },
38+
// },
4039
],
4140

4241
/* Run your local dev server before starting the tests */
43-
// webServer: {
44-
// command: 'npm run start',
45-
// url: 'http://127.0.0.1:3000',
46-
// reuseExistingServer: !process.env.CI,
47-
// },
42+
webServer: {
43+
command: 'pnpm run dev',
44+
url: 'http://localhost:3001',
45+
reuseExistingServer: false,
46+
env: {
47+
PORT: '3001',
48+
VITE_PARSEABLE_URL: 'https://demo.parseable.com',
49+
VITE_USE_BASIC_AUTH: 'true',
50+
VITE_USERNAME: 'admin',
51+
VITE_PASSWORD: 'admin',
52+
},
53+
},
4854
});

src/pages/Stream/components/EventTimeLineGraph.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const { makeTimeRangeLabel } = timeRangeUtils;
2020
type CompactInterval = 'minute' | 'day' | 'hour' | 'quarter-hour' | 'half-hour' | 'month';
2121

2222
function extractWhereClause(sql: string) {
23-
const whereClauseRegex = /WHERE\s+(.*?)(?=\s*(ORDER\s+BY|GROUP\s+BY|LIMIT|$))/i;
23+
const whereClauseRegex = /WHERE\s+(.*?)(?=\s*(ORDER\s+BY|GROUP\s+BY|OFFSET|LIMIT|$))/i;
2424
const match = sql.match(whereClauseRegex);
2525
if (match) {
2626
return match[1].trim();

src/pages/Stream/components/PrimaryToolbar.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import classes from './styles/PrimaryToolbar.module.css';
2121
const { toggleDeleteModal, onToggleView } = logsStoreReducers;
2222
const { toggleSavedFiltersModal } = filterStoreReducers;
2323
const renderMaximizeIcon = () => <IconMaximize size={px('1rem')} stroke={1.5} />;
24-
const renderDeleteIcon = () => <IconTrash size={px('1rem')} stroke={1.5} />;
24+
const renderDeleteIcon = () => <IconTrash data-id="delete-stream-btn" size={px('1rem')} stroke={1.5} />;
2525

2626
const MaximizeButton = () => {
2727
const [_appStore, setAppStore] = useAppStore((_store) => null);

src/pages/Stream/components/Sidebar.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ const ConfigButton = (props: MenuItemProps) => {
4343
<Stack
4444
onClick={() => props.setCurrentView(viewName)}
4545
style={{ padding: '4px 0', alignItems: 'center' }}
46-
className={classes.menuItemContainer}>
46+
className={classes.menuItemContainer}
47+
data-id="manage-stream-btn">
4748
<Tooltip label="Manage" position="right">
4849
<Stack className={additionalClassNames} style={{ padding: '4px 4px' }}>
4950
<IconSettings2

tests/home.spec.ts

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
const TEST_URL = 'http://localhost:3001';
4+
5+
test.describe('Home Page', () => {
6+
test.beforeEach(async ({ page }) => {
7+
await page.goto(`${TEST_URL}`);
8+
});
9+
test('should render the component correctly with default state', async ({ page }) => {
10+
await expect(page.locator('text=All Streams')).toBeVisible();
11+
await expect(page.locator('input[placeholder="Search Stream"]')).toBeVisible();
12+
});
13+
14+
test('should render the component correctly with streams data', async ({ page }) => {
15+
// Check that the streams are displayed
16+
await expect(page.locator('text=backend')).toBeVisible();
17+
await expect(page.locator('text=frontend')).toBeVisible();
18+
});
19+
20+
test('should filter streams based on search input', async ({ page }) => {
21+
const searchInput = page.locator('input[placeholder="Search Stream"]');
22+
await searchInput.fill('backend');
23+
await expect(page.locator('text=backend')).toBeVisible();
24+
25+
// Clear the search and check if both streams are visible
26+
await searchInput.fill('');
27+
await expect(page.locator('text=backend')).toBeVisible();
28+
await expect(page.locator('text=frontend')).toBeVisible();
29+
});
30+
31+
test('should display empty state if no streams are available', async ({ page }) => {
32+
const searchInput = page.locator('input[placeholder="Search Stream"]');
33+
await searchInput.fill(Math.random().toString());
34+
35+
// Expect empty state view to be visible
36+
await expect(page.locator('text=No Stream found on this account')).toBeVisible();
37+
await expect(page.locator('text=All Streams (0)')).toBeVisible();
38+
});
39+
40+
test('should display the create stream button', async ({ page }) => {
41+
const createButton = page.locator('text=Create Stream');
42+
await expect(createButton).toBeVisible();
43+
});
44+
45+
test('should display the create stream modal on click', async ({ page }) => {
46+
const createButton = page.locator('text=Create Stream');
47+
await expect(createButton).toBeVisible();
48+
await createButton.click();
49+
await expect(page.locator('text=Create Stream').nth(1)).toBeVisible();
50+
});
51+
52+
test.describe('Create Stream Modal', () => {
53+
test.beforeEach(async ({ page }) => {
54+
const createButton = page.locator('text=Create Stream');
55+
await createButton.click();
56+
});
57+
58+
test('should render the form correctly', async ({ page }) => {
59+
// Check if essential elements are present in the form
60+
await expect(page.locator('input[placeholder="Name"]')).toBeVisible();
61+
await expect(page.locator('text=Schema Type')).toBeVisible();
62+
await expect(page.locator('button:has-text("Create")').nth(1)).toBeVisible();
63+
});
64+
65+
test('should enable the submit button when form is valid', async ({ page }) => {
66+
// Fill in the form with valid values
67+
await page.fill('input[placeholder="Name"]', 'PlaywrightStream');
68+
69+
await page.locator('button:has-text("Create")').nth(1).click();
70+
await page.waitForTimeout(1000);
71+
});
72+
73+
test.describe('Delete Stream', () => {
74+
test('search, navigate, and delete demo stream', async ({ page }) => {
75+
// Step 1: Search for the demo stream
76+
await page.goto(`${TEST_URL}`);
77+
const searchInput = page.locator('input[placeholder="Search Stream"]');
78+
await searchInput.fill('PlaywrightStream');
79+
await expect(page.locator('text=PlaywrightStream')).toBeVisible();
80+
81+
// Step 2: Navigate to the demo stream
82+
await page.locator('text=PlaywrightStream').click();
83+
const manageBtn = page.locator('[data-id="manage-stream-btn"]');
84+
await expect(manageBtn).toBeVisible();
85+
await manageBtn.click();
86+
87+
await page.waitForTimeout(1000);
88+
89+
// Step 3: Delete the demo stream
90+
const deleteBtn = page.locator('[data-id="delete-stream-btn"]');
91+
await expect(deleteBtn).toBeVisible();
92+
await deleteBtn.click();
93+
await expect(page.locator('text=Delete Stream')).toBeVisible();
94+
await page.fill('input[placeholder*="Type the name of the stream to confirm. i.e."]', 'PlaywrightStream');
95+
const confirmDeleteBtn = page.getByRole('button', { name: 'Delete' });
96+
await expect(confirmDeleteBtn).toBeEnabled();
97+
await confirmDeleteBtn.click();
98+
99+
await page.waitForTimeout(1000);
100+
101+
// Step 4: Check if stream is deleted
102+
await page.goto(`${TEST_URL}`);
103+
await expect(searchInput).toBeVisible();
104+
await searchInput.fill('PlaywrightStream');
105+
await expect(page.locator('text=No Stream found on this account')).toBeVisible();
106+
await expect(page.locator('text=All Streams (0)')).toBeVisible();
107+
});
108+
});
109+
});
110+
});

0 commit comments

Comments
 (0)