From ca1bbc8901df4a9b6ddb4b175d2a40641ba3c5ef Mon Sep 17 00:00:00 2001
From: Praveen K B <30530587+praveen5959@users.noreply.github.com>
Date: Fri, 15 Nov 2024 17:42:27 +0530
Subject: [PATCH 1/8] chore: add home page automation tests (#370)
---
.env.example | 3 +
.github/workflows/playwright.yml | 5 -
playwright.config.ts | 40 ++++---
.../Stream/components/EventTimeLineGraph.tsx | 2 +-
.../Stream/components/PrimaryToolbar.tsx | 2 +-
src/pages/Stream/components/Sidebar.tsx | 3 +-
tests/home.spec.ts | 110 ++++++++++++++++++
7 files changed, 140 insertions(+), 25 deletions(-)
create mode 100644 tests/home.spec.ts
diff --git a/.env.example b/.env.example
index 7663c9be..1725b318 100644
--- a/.env.example
+++ b/.env.example
@@ -1 +1,4 @@
VITE_PARSEABLE_URL="https://demo.parseable.com"
+VITE_USE_BASIC_AUTH=true
+VITE_USERNAME=admin
+VITE_PASSWORD=admin
\ No newline at end of file
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index e6e1d02f..96eb75f9 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -17,11 +17,6 @@ jobs:
run: npm install -g pnpm && pnpm install
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
- - name: Start the development server
- run: pnpm run dev &
- env:
- PORT: 3001
- VITE_PARSEABLE_URL: 'https://demo.parseable.com'
- name: Run Playwright tests
run: pnpm exec playwright test
- uses: actions/upload-artifact@v4
diff --git a/playwright.config.ts b/playwright.config.ts
index c607f68f..5c90d80b 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -2,8 +2,9 @@ import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
+ testIgnore: '**/login.spec.ts',
/* Run tests in files in parallel */
- fullyParallel: true,
+ fullyParallel: true, // Set this to false to ensure sequential execution of files
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
@@ -11,7 +12,7 @@ export default defineConfig({
/* 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',
+ reporter: 'line',
/* 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('/')`. */
@@ -27,22 +28,27 @@ export default defineConfig({
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
-
- {
- name: 'firefox',
- use: { ...devices['Desktop Firefox'] },
- },
-
- {
- name: 'webkit',
- use: { ...devices['Desktop Safari'] },
- },
+ // {
+ // name: 'Firefox',
+ // use: { ...devices['Desktop Firefox'] },
+ // },
+ // {
+ // name: 'Safari',
+ // use: { ...devices['Desktop Safari'] },
+ // },
],
/* Run your local dev server before starting the tests */
- // webServer: {
- // command: 'npm run start',
- // url: 'http://127.0.0.1:3000',
- // reuseExistingServer: !process.env.CI,
- // },
+ webServer: {
+ command: 'pnpm run dev',
+ url: 'http://localhost:3001',
+ reuseExistingServer: false,
+ env: {
+ PORT: '3001',
+ VITE_PARSEABLE_URL: 'https://demo.parseable.com',
+ VITE_USE_BASIC_AUTH: 'true',
+ VITE_USERNAME: 'admin',
+ VITE_PASSWORD: 'admin',
+ },
+ },
});
diff --git a/src/pages/Stream/components/EventTimeLineGraph.tsx b/src/pages/Stream/components/EventTimeLineGraph.tsx
index aafc6fe1..51d56b0c 100644
--- a/src/pages/Stream/components/EventTimeLineGraph.tsx
+++ b/src/pages/Stream/components/EventTimeLineGraph.tsx
@@ -20,7 +20,7 @@ const { makeTimeRangeLabel } = timeRangeUtils;
type CompactInterval = 'minute' | 'day' | 'hour' | 'quarter-hour' | 'half-hour' | 'month';
function extractWhereClause(sql: string) {
- const whereClauseRegex = /WHERE\s+(.*?)(?=\s*(ORDER\s+BY|GROUP\s+BY|LIMIT|$))/i;
+ const whereClauseRegex = /WHERE\s+(.*?)(?=\s*(ORDER\s+BY|GROUP\s+BY|OFFSET|LIMIT|$))/i;
const match = sql.match(whereClauseRegex);
if (match) {
return match[1].trim();
diff --git a/src/pages/Stream/components/PrimaryToolbar.tsx b/src/pages/Stream/components/PrimaryToolbar.tsx
index 3e67bb07..16e41568 100644
--- a/src/pages/Stream/components/PrimaryToolbar.tsx
+++ b/src/pages/Stream/components/PrimaryToolbar.tsx
@@ -21,7 +21,7 @@ import classes from './styles/PrimaryToolbar.module.css';
const { toggleDeleteModal, onToggleView } = logsStoreReducers;
const { toggleSavedFiltersModal } = filterStoreReducers;
const renderMaximizeIcon = () => ;
-const renderDeleteIcon = () => ;
+const renderDeleteIcon = () => ;
const MaximizeButton = () => {
const [_appStore, setAppStore] = useAppStore((_store) => null);
diff --git a/src/pages/Stream/components/Sidebar.tsx b/src/pages/Stream/components/Sidebar.tsx
index 7fdf1ca9..c94ab723 100644
--- a/src/pages/Stream/components/Sidebar.tsx
+++ b/src/pages/Stream/components/Sidebar.tsx
@@ -43,7 +43,8 @@ const ConfigButton = (props: MenuItemProps) => {
props.setCurrentView(viewName)}
style={{ padding: '4px 0', alignItems: 'center' }}
- className={classes.menuItemContainer}>
+ className={classes.menuItemContainer}
+ data-id="manage-stream-btn">
{
+ test.beforeEach(async ({ page }) => {
+ await page.goto(`${TEST_URL}`);
+ });
+ test('should render the component correctly with default state', async ({ page }) => {
+ await expect(page.locator('text=All Streams')).toBeVisible();
+ await expect(page.locator('input[placeholder="Search Stream"]')).toBeVisible();
+ });
+
+ test('should render the component correctly with streams data', async ({ page }) => {
+ // Check that the streams are displayed
+ await expect(page.locator('text=backend')).toBeVisible();
+ await expect(page.locator('text=frontend')).toBeVisible();
+ });
+
+ test('should filter streams based on search input', async ({ page }) => {
+ const searchInput = page.locator('input[placeholder="Search Stream"]');
+ await searchInput.fill('backend');
+ await expect(page.locator('text=backend')).toBeVisible();
+
+ // Clear the search and check if both streams are visible
+ await searchInput.fill('');
+ await expect(page.locator('text=backend')).toBeVisible();
+ await expect(page.locator('text=frontend')).toBeVisible();
+ });
+
+ test('should display empty state if no streams are available', async ({ page }) => {
+ const searchInput = page.locator('input[placeholder="Search Stream"]');
+ await searchInput.fill(Math.random().toString());
+
+ // Expect empty state view to be visible
+ await expect(page.locator('text=No Stream found on this account')).toBeVisible();
+ await expect(page.locator('text=All Streams (0)')).toBeVisible();
+ });
+
+ test('should display the create stream button', async ({ page }) => {
+ const createButton = page.locator('text=Create Stream');
+ await expect(createButton).toBeVisible();
+ });
+
+ test('should display the create stream modal on click', async ({ page }) => {
+ const createButton = page.locator('text=Create Stream');
+ await expect(createButton).toBeVisible();
+ await createButton.click();
+ await expect(page.locator('text=Create Stream').nth(1)).toBeVisible();
+ });
+
+ test.describe('Create Stream Modal', () => {
+ test.beforeEach(async ({ page }) => {
+ const createButton = page.locator('text=Create Stream');
+ await createButton.click();
+ });
+
+ test('should render the form correctly', async ({ page }) => {
+ // Check if essential elements are present in the form
+ await expect(page.locator('input[placeholder="Name"]')).toBeVisible();
+ await expect(page.locator('text=Schema Type')).toBeVisible();
+ await expect(page.locator('button:has-text("Create")').nth(1)).toBeVisible();
+ });
+
+ test('should enable the submit button when form is valid', async ({ page }) => {
+ // Fill in the form with valid values
+ await page.fill('input[placeholder="Name"]', 'PlaywrightStream');
+
+ await page.locator('button:has-text("Create")').nth(1).click();
+ await page.waitForTimeout(1000);
+ });
+
+ test.describe('Delete Stream', () => {
+ test('search, navigate, and delete demo stream', async ({ page }) => {
+ // Step 1: Search for the demo stream
+ await page.goto(`${TEST_URL}`);
+ const searchInput = page.locator('input[placeholder="Search Stream"]');
+ await searchInput.fill('PlaywrightStream');
+ await expect(page.locator('text=PlaywrightStream')).toBeVisible();
+
+ // Step 2: Navigate to the demo stream
+ await page.locator('text=PlaywrightStream').click();
+ const manageBtn = page.locator('[data-id="manage-stream-btn"]');
+ await expect(manageBtn).toBeVisible();
+ await manageBtn.click();
+
+ await page.waitForTimeout(1000);
+
+ // Step 3: Delete the demo stream
+ const deleteBtn = page.locator('[data-id="delete-stream-btn"]');
+ await expect(deleteBtn).toBeVisible();
+ await deleteBtn.click();
+ await expect(page.locator('text=Delete Stream')).toBeVisible();
+ await page.fill('input[placeholder*="Type the name of the stream to confirm. i.e."]', 'PlaywrightStream');
+ const confirmDeleteBtn = page.getByRole('button', { name: 'Delete' });
+ await expect(confirmDeleteBtn).toBeEnabled();
+ await confirmDeleteBtn.click();
+
+ await page.waitForTimeout(1000);
+
+ // Step 4: Check if stream is deleted
+ await page.goto(`${TEST_URL}`);
+ await expect(searchInput).toBeVisible();
+ await searchInput.fill('PlaywrightStream');
+ await expect(page.locator('text=No Stream found on this account')).toBeVisible();
+ await expect(page.locator('text=All Streams (0)')).toBeVisible();
+ });
+ });
+ });
+});
From 72976160a9c0cc3db93b7a6bae02929c877fdcb5 Mon Sep 17 00:00:00 2001
From: Pranav Goel <48860494+pranavgoel29@users.noreply.github.com>
Date: Sat, 16 Nov 2024 16:47:53 +0530
Subject: [PATCH 2/8] chore: add users roles pages tests automation (#373)
---
package.json | 4 +-
src/components/Button/IconButton.tsx | 3 +
src/hooks/useGetStreamMetadata.ts | 4 +-
src/pages/AccessManagement/PrivilegeTR.tsx | 2 +-
src/pages/AccessManagement/Roles.tsx | 16 +-
src/pages/Dashboards/CreateTileForm.tsx | 6 +-
src/pages/Dashboards/assets/templates.tsx | 12 +-
.../Stream/Views/Explore/StaticLogTable.tsx | 2 +-
src/pages/Stream/Views/Manage/Alerts.tsx | 4 +-
src/pages/Stream/providers/LogsProvider.tsx | 17 +-
src/pages/Stream/providers/StreamProvider.tsx | 2 +-
src/pages/Systems/ServerList.tsx | 4 +-
tests/helpers/users_roles.ts | 75 +++++++
tests/users_roles.spec.ts | 185 ++++++++++++++++++
tsconfig.json | 25 ++-
15 files changed, 323 insertions(+), 38 deletions(-)
create mode 100644 tests/helpers/users_roles.ts
create mode 100644 tests/users_roles.spec.ts
diff --git a/package.json b/package.json
index 161efca0..17a973a6 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,9 @@
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"start": "vite preview --host --port 3002",
"tsCheck": "tsc --noEmit",
- "pq": "pretty-quick"
+ "pq": "pretty-quick",
+ "test": "playwright test",
+ "test-ui": "playwright test --ui"
},
"dependencies": {
"@apache-arrow/ts": "^14.0.2",
diff --git a/src/components/Button/IconButton.tsx b/src/components/Button/IconButton.tsx
index ef64bdb3..26d5292d 100644
--- a/src/components/Button/IconButton.tsx
+++ b/src/components/Button/IconButton.tsx
@@ -5,6 +5,7 @@ import classes from './Button.module.css';
type IconButtonProps = {
onClick?: () => void;
renderIcon: () => ReactNode;
+ data_id?: string;
icon?: ReactNode;
active?: boolean;
tooltipLabel?: string;
@@ -17,6 +18,7 @@ const IconButton: FC = (props) => {
return (
@@ -27,6 +29,7 @@ const IconButton: FC = (props) => {
} else {
return (
diff --git a/src/hooks/useGetStreamMetadata.ts b/src/hooks/useGetStreamMetadata.ts
index bb2c3da7..7619b89f 100644
--- a/src/hooks/useGetStreamMetadata.ts
+++ b/src/hooks/useGetStreamMetadata.ts
@@ -15,8 +15,8 @@ export type MetaData = {
// until dedicated endpoint been provided - fetch one by one
export const useGetStreamMetadata = () => {
- const [isLoading, setLoading] = useState(true);
- const [error, setError] = useState(false);
+ const [isLoading, setLoading] = useState(true);
+ const [error, setError] = useState(false);
const [metaData, setMetadata] = useState(null);
const [userRoles] = useAppStore((store) => store.userRoles);
diff --git a/src/pages/AccessManagement/PrivilegeTR.tsx b/src/pages/AccessManagement/PrivilegeTR.tsx
index d20b77c1..e91d75c4 100644
--- a/src/pages/AccessManagement/PrivilegeTR.tsx
+++ b/src/pages/AccessManagement/PrivilegeTR.tsx
@@ -274,7 +274,7 @@ const PrivilegeTR: FC = (props) => {
)}
-
+
@@ -207,6 +210,7 @@ const Roles: FC = () => {
onClose={handleDefaultRoleModalClose}
title="Set default oidc role"
centered
+ styles={{ title: { fontWeight: 500 } }}
className={classes.modalStyle}>
) : (
diff --git a/tests/helpers/users_roles.ts b/tests/helpers/users_roles.ts
index 0ecbe84b..f665d830 100644
--- a/tests/helpers/users_roles.ts
+++ b/tests/helpers/users_roles.ts
@@ -61,7 +61,7 @@ const deleteIfRoleExists = async (page: Page, ROLE_NAME: string) => {
await deleteButton.click();
await page.waitForSelector('text=Are you sure you want to delete this role?');
- const roleNameInput = page.getByPlaceholder('Please enter the Role');
+ const roleNameInput = page.getByPlaceholder('Type the role name');
await roleNameInput.fill(ROLE_NAME);
const confirmDeleteButton = page.getByRole('button', { name: 'Delete' });
diff --git a/tests/home.spec.ts b/tests/home.spec.ts
index a9b38ea8..ff4856b4 100644
--- a/tests/home.spec.ts
+++ b/tests/home.spec.ts
@@ -91,7 +91,7 @@ test.describe('Home Page', () => {
await expect(deleteBtn).toBeVisible();
await deleteBtn.click();
await expect(page.locator('text=Delete Stream')).toBeVisible();
- await page.fill('input[placeholder*="Type the name of the stream to confirm. i.e."]', 'PlaywrightStream');
+ await page.fill('input[placeholder*="Type the name of the stream"]', 'PlaywrightStream');
const confirmDeleteBtn = page.getByRole('button', { name: 'Delete' });
await expect(confirmDeleteBtn).toBeEnabled();
await confirmDeleteBtn.click();