mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-15 20:01:28 +00:00
Merge pull request #6070 from usebruno/feature/collection-test-results-and-filtering-internal
Feature/collection test results and filtering internal
This commit is contained in:
@@ -3,16 +3,13 @@ import path from 'utils/common/path';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { get, cloneDeep } from 'lodash';
|
||||
import { runCollectionFolder, cancelRunnerExecution, mountCollection, updateRunnerConfiguration } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { resetCollectionRunner } from 'providers/ReduxStore/slices/collections';
|
||||
import { findItemInCollection, getTotalRequestCountInCollection } from 'utils/collections';
|
||||
import { IconRefresh, IconCircleCheck, IconCircleX, IconCircleOff, IconCheck, IconX, IconRun, IconLoader2 } from '@tabler/icons';
|
||||
import { resetCollectionRunner, updateRunnerTagsDetails } from 'providers/ReduxStore/slices/collections';
|
||||
import { findItemInCollection, getTotalRequestCountInCollection, areItemsLoading, getRequestItemsForCollectionRun } from 'utils/collections';
|
||||
import { IconRefresh, IconCircleCheck, IconCircleX, IconCircleOff, IconCheck, IconX, IconRun, IconExternalLink } from '@tabler/icons';
|
||||
import ResponsePane from './ResponsePane';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { areItemsLoading } from 'utils/collections';
|
||||
import RunnerTags from './RunnerTags/index';
|
||||
import RunConfigurationPanel from './RunConfigurationPanel';
|
||||
import { getRequestItemsForCollectionRun } from 'utils/collections/index';
|
||||
import { updateRunnerTagsDetails } from 'providers/ReduxStore/slices/collections/index';
|
||||
|
||||
const getDisplayName = (fullPath, pathname, name = '') => {
|
||||
let relativePath = path.relative(fullPath, pathname);
|
||||
@@ -42,49 +39,61 @@ const anyTestFailed = (item) => {
|
||||
item.postResponseTestStatus === 'fail';
|
||||
};
|
||||
|
||||
// === Centralized filters definition ===
|
||||
const FILTERS = {
|
||||
all: {
|
||||
label: 'All',
|
||||
predicate: () => true,
|
||||
resultFilter: (results) => results
|
||||
},
|
||||
passed: {
|
||||
label: 'Passed',
|
||||
predicate: (item) => allTestsPassed(item),
|
||||
resultFilter: (results) => results?.filter((r) => r.status === 'pass')
|
||||
},
|
||||
failed: {
|
||||
label: 'Failed',
|
||||
predicate: (item) => anyTestFailed(item),
|
||||
resultFilter: (results) => results?.filter((r) => ['fail', 'error'].includes(r.status))
|
||||
},
|
||||
skipped: {
|
||||
label: 'Skipped',
|
||||
predicate: (item) => item.status === 'skipped',
|
||||
resultFilter: (results) => results
|
||||
}
|
||||
};
|
||||
|
||||
// === Reusable filter button ===
|
||||
const FilterButton = ({ label, count, active, onClick }) => (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`font-medium transition-colors cursor-pointer flex items-center gap-1.5 border-b-2 pb-2 ${
|
||||
active
|
||||
? 'text-[#343434] dark:text-[#CCCCCC] border-[#F59E0B]'
|
||||
: 'text-[#989898] dark:text-[#CCCCCC80] border-transparent'
|
||||
}`}
|
||||
style={{ fontFamily: 'Inter', fontSize: '14px', fontWeight: 500, lineHeight: '100%', letterSpacing: '0%' }}
|
||||
>
|
||||
{label}
|
||||
<span
|
||||
className="px-[4.5px] py-[2px] rounded-[2px] bg-[#F7F7F7] dark:bg-[#242424] border border-[#EFEFEF] dark:border-[#92929233] text-[#989898] dark:text-inherit"
|
||||
style={{ borderWidth: '1px', fontFamily: 'Inter', fontSize: '10px', fontWeight: 500, lineHeight: '100%', letterSpacing: '0%' }}
|
||||
>
|
||||
{count}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
export default function RunnerResults({ collection }) {
|
||||
const dispatch = useDispatch();
|
||||
const [selectedItem, setSelectedItem] = useState(null);
|
||||
const [delay, setDelay] = useState(null);
|
||||
const [activeFilter, setActiveFilter] = useState('all');
|
||||
const [selectedRequestItems, setSelectedRequestItems] = useState([]);
|
||||
const [configureMode, setConfigureMode] = useState(false);
|
||||
|
||||
// ref for the runner output body
|
||||
const runnerBodyRef = useRef();
|
||||
|
||||
const autoScrollRunnerBody = () => {
|
||||
if (runnerBodyRef?.current) {
|
||||
// mimics the native terminal scroll style
|
||||
runnerBodyRef.current.scrollTo(0, 100000);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!collection.runnerResult) {
|
||||
setSelectedItem(null);
|
||||
}
|
||||
autoScrollRunnerBody();
|
||||
}, [collection, setSelectedItem]);
|
||||
|
||||
useEffect(() => {
|
||||
const runnerInfo = get(collection, 'runnerResult.info', {});
|
||||
if (runnerInfo.status === 'running') {
|
||||
setConfigureMode(false);
|
||||
}
|
||||
}, [collection.runnerResult]);
|
||||
|
||||
useEffect(() => {
|
||||
const savedConfiguration = get(collection, 'runnerConfiguration', null);
|
||||
if (savedConfiguration) {
|
||||
if (savedConfiguration.selectedRequestItems && configureMode) {
|
||||
setSelectedRequestItems(savedConfiguration.selectedRequestItems);
|
||||
}
|
||||
if (savedConfiguration.delay !== undefined && delay === null) {
|
||||
setDelay(savedConfiguration.delay);
|
||||
}
|
||||
}
|
||||
}, [collection.runnerConfiguration, configureMode, delay]);
|
||||
|
||||
const collectionCopy = cloneDeep(collection);
|
||||
const runnerInfo = get(collection, 'runnerResult.info', {});
|
||||
|
||||
@@ -126,6 +135,63 @@ export default function RunnerResults({ collection }) {
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const activeFilterConfig = FILTERS[activeFilter];
|
||||
const filteredItems = items.filter(activeFilterConfig.predicate);
|
||||
|
||||
const filterTestResults = (results) => {
|
||||
if (!results || !Array.isArray(results)) return [];
|
||||
return activeFilterConfig.resultFilter(results);
|
||||
};
|
||||
|
||||
const autoScrollRunnerBody = () => {
|
||||
if (runnerBodyRef?.current) {
|
||||
const element = runnerBodyRef.current;
|
||||
const scrollThreshold = 100; // pixels from bottom to consider "at bottom"
|
||||
const isNearBottom
|
||||
= element.scrollHeight - element.scrollTop - element.clientHeight < scrollThreshold;
|
||||
|
||||
// Only auto-scroll if user is already near the bottom
|
||||
if (isNearBottom) {
|
||||
// mimics the native terminal scroll style
|
||||
element.scrollTo(0, 100000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!collection.runnerResult) {
|
||||
setSelectedItem(null);
|
||||
}
|
||||
autoScrollRunnerBody();
|
||||
}, [collection, setSelectedItem]);
|
||||
|
||||
useEffect(() => {
|
||||
// Auto-scroll when items are added or updated during execution
|
||||
// Only scrolls if user is already at/near the bottom
|
||||
if (filteredItems.length > 0) {
|
||||
autoScrollRunnerBody();
|
||||
}
|
||||
}, [filteredItems]);
|
||||
|
||||
useEffect(() => {
|
||||
const runnerInfo = get(collection, 'runnerResult.info', {});
|
||||
if (runnerInfo.status === 'running') {
|
||||
setConfigureMode(false);
|
||||
}
|
||||
}, [collection.runnerResult]);
|
||||
|
||||
useEffect(() => {
|
||||
const savedConfiguration = get(collection, 'runnerConfiguration', null);
|
||||
if (savedConfiguration) {
|
||||
if (savedConfiguration.selectedRequestItems && configureMode) {
|
||||
setSelectedRequestItems(savedConfiguration.selectedRequestItems);
|
||||
}
|
||||
if (savedConfiguration.delay !== undefined && delay === null) {
|
||||
setDelay(savedConfiguration.delay);
|
||||
}
|
||||
}
|
||||
}, [collection.runnerConfiguration, configureMode, delay]);
|
||||
|
||||
const ensureCollectionIsMounted = () => {
|
||||
if(collection.mountStatus === 'mounted'){
|
||||
return;
|
||||
@@ -192,14 +258,14 @@ export default function RunnerResults({ collection }) {
|
||||
}, [tagsEnabled]);
|
||||
|
||||
const totalRequestsInCollection = getTotalRequestCountInCollection(collectionCopy);
|
||||
const passedRequests = items.filter(allTestsPassed);
|
||||
const failedRequests = items.filter(anyTestFailed);
|
||||
const filterCounts = {
|
||||
all: items.length,
|
||||
passed: items.filter(allTestsPassed).length,
|
||||
failed: items.filter(anyTestFailed).length,
|
||||
skipped: items.filter((i) => i.status === 'skipped').length
|
||||
};
|
||||
|
||||
const skippedRequests = items.filter((item) => {
|
||||
return item.status === 'skipped';
|
||||
});
|
||||
let isCollectionLoading = areItemsLoading(collection);
|
||||
|
||||
if (!items || !items.length) {
|
||||
return (
|
||||
<StyledWrapper className="pl-4 overflow-hidden h-full">
|
||||
@@ -285,27 +351,57 @@ export default function RunnerResults({ collection }) {
|
||||
|
||||
return (
|
||||
<StyledWrapper className="px-4 pb-4 flex flex-grow flex-col relative overflow-auto">
|
||||
<div className="flex items-center my-6 flex-row">
|
||||
<div className="font-medium title flex items-center">
|
||||
Runner
|
||||
<IconRun size={20} strokeWidth={1.5} className="ml-2" />
|
||||
{/* Filter Bar and Actions */}
|
||||
<div className="flex items-center justify-between mb-4 pt-[14px] gap-4">
|
||||
<div className="flex items-stretch rounded-lg border border-[#EFEFEF] dark:border-[#92929233] max-h-[35px] flex-shrink-0" style={{ borderWidth: '1px' }}>
|
||||
<div className="flex items-center px-3 py-2 rounded-l-lg bg-[#F3F3F3] dark:bg-[#2B2D2F]">
|
||||
<span className="text-gray-600 dark:text-gray-400" style={{ fontFamily: 'Inter', fontSize: '14px', fontWeight: 400 }}>
|
||||
Filter by:
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-5 px-3 pt-2 pb-0 rounded-r-lg bg-transparent dark:bg-transparent">
|
||||
{Object.entries(FILTERS).map(([key, { label }]) => (
|
||||
<FilterButton
|
||||
key={key}
|
||||
label={label}
|
||||
count={filterCounts[key]}
|
||||
active={activeFilter === key}
|
||||
onClick={() => setActiveFilter(key)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{runnerInfo.status !== 'ended' && runnerInfo.cancelTokenUid && (
|
||||
<button className="btn btn-sm btn-danger" onClick={cancelExecution}>
|
||||
Cancel Execution
|
||||
</button>
|
||||
)}
|
||||
|
||||
{runnerInfo.status !== 'ended' && runnerInfo.cancelTokenUid ? (
|
||||
<div className="flex items-center flex-shrink-0">
|
||||
<button className="btn btn-sm btn-danger" onClick={cancelExecution}>Cancel Execution</button>
|
||||
</div>
|
||||
) : runnerInfo.status === 'ended' ? (
|
||||
<div className="flex items-center gap-3 flex-shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
className="px-3 py-1.5 rounded-md bg-transparent border border-[#989898] dark:border-[#444444] text-[#989898] hover:opacity-80 transition-colors"
|
||||
style={{ fontFamily: 'Inter', fontSize: '12px', fontWeight: 500 }}
|
||||
onClick={runAgain}
|
||||
>
|
||||
Run Again
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="px-3 py-1.5 rounded-md bg-transparent border border-[#989898] dark:border-[#444444] text-[#989898] hover:opacity-80 transition-colors"
|
||||
style={{ fontFamily: 'Inter', fontSize: '12px', fontWeight: 500 }}
|
||||
onClick={resetRunner}
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 h-[calc(100vh_-_10rem)] overflow-hidden">
|
||||
<div
|
||||
className={`flex flex-col overflow-y-auto ${selectedItem || (configureMode && !selectedItem && !runnerInfo.status === 'running') ? 'w-1/2' : 'w-full'}`}
|
||||
ref={runnerBodyRef}
|
||||
className="flex flex-col w-1/2"
|
||||
>
|
||||
<div className="pb-2 font-medium test-summary">
|
||||
Total Requests: {items.length}, Passed: {passedRequests.length}, Failed: {failedRequests.length}, Skipped:{' '}
|
||||
{skippedRequests.length}
|
||||
</div>
|
||||
{tagsEnabled && areTagsAdded && (
|
||||
<div className="pb-2 text-xs flex flex-row gap-1">
|
||||
Tags:
|
||||
@@ -326,8 +422,8 @@ export default function RunnerResults({ collection }) {
|
||||
: null}
|
||||
|
||||
{/* Items list */}
|
||||
<div className="overflow-y-auto flex-1">
|
||||
{items.map((item) => {
|
||||
<div className="overflow-y-auto flex-1 " ref={runnerBodyRef}>
|
||||
{filteredItems.map((item) => {
|
||||
return (
|
||||
<div key={item.uid}>
|
||||
<div className="item-path mt-2">
|
||||
@@ -371,7 +467,7 @@ export default function RunnerResults({ collection }) {
|
||||
|
||||
<ul className="pl-8">
|
||||
{item.preRequestTestResults
|
||||
? item.preRequestTestResults.map((result) => (
|
||||
? filterTestResults(item.preRequestTestResults).map((result) => (
|
||||
<li key={result.uid}>
|
||||
{result.status === 'pass' ? (
|
||||
<span className="test-success flex items-center">
|
||||
@@ -391,7 +487,7 @@ export default function RunnerResults({ collection }) {
|
||||
))
|
||||
: null}
|
||||
{item.postResponseTestResults
|
||||
? item.postResponseTestResults.map((result) => (
|
||||
? filterTestResults(item.postResponseTestResults).map((result) => (
|
||||
<li key={result.uid}>
|
||||
{result.status === 'pass' ? (
|
||||
<span className="test-success flex items-center">
|
||||
@@ -411,7 +507,7 @@ export default function RunnerResults({ collection }) {
|
||||
))
|
||||
: null}
|
||||
{item.testResults
|
||||
? item.testResults.map((result) => (
|
||||
? filterTestResults(item.testResults).map((result) => (
|
||||
<li key={result.uid}>
|
||||
{result.status === 'pass' ? (
|
||||
<span className="test-success flex items-center">
|
||||
@@ -430,7 +526,7 @@ export default function RunnerResults({ collection }) {
|
||||
</li>
|
||||
))
|
||||
: null}
|
||||
{item.assertionResults?.map((result) => (
|
||||
{filterTestResults(item.assertionResults).map((result) => (
|
||||
<li key={result.uid}>
|
||||
{result.status === 'pass' ? (
|
||||
<span className="test-success flex items-center">
|
||||
@@ -454,42 +550,50 @@ export default function RunnerResults({ collection }) {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{runnerInfo.status === 'ended' ? (
|
||||
<div className="mt-2 mb-4">
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary mt-6" onClick={runAgain}>
|
||||
Run Again
|
||||
</button>
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary mt-6 ml-3" disabled={shouldDisableCollectionRun} onClick={runCollection}>
|
||||
Run Collection
|
||||
</button>
|
||||
<button className="btn btn-sm btn-close mt-6 ml-3" onClick={resetRunner}>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{selectedItem ? (
|
||||
<div className="flex flex-1 w-[50%] overflow-y-auto">
|
||||
<div className="flex flex-col w-full overflow-hidden">
|
||||
<div className="flex items-center mb-4 font-medium">
|
||||
<span className="mr-2">{selectedItem.displayName}</span>
|
||||
<span>
|
||||
{allTestsPassed(selectedItem) ?
|
||||
<IconCircleCheck className="test-success" size={20} strokeWidth={1.5} />
|
||||
: null}
|
||||
{anyTestFailed(selectedItem) ?
|
||||
<IconCircleX className="test-failure" size={20} strokeWidth={1.5} />
|
||||
: null}
|
||||
{selectedItem.status === 'skipped' ?
|
||||
<IconCircleOff className="skipped-request" size={20} strokeWidth={1.5} />
|
||||
: null}
|
||||
</span>
|
||||
<div className="flex items-center justify-between mb-4 font-medium">
|
||||
<div className="flex items-center">
|
||||
<span className="mr-2">{selectedItem.displayName}</span>
|
||||
<span>
|
||||
{allTestsPassed(selectedItem)
|
||||
? <IconCircleCheck className="test-success" size={20} strokeWidth={1.5} />
|
||||
: null}
|
||||
{anyTestFailed(selectedItem)
|
||||
? <IconCircleX className="test-failure" size={20} strokeWidth={1.5} />
|
||||
: null}
|
||||
{selectedItem.status === 'skipped'
|
||||
? <IconCircleOff className="skipped-request" size={20} strokeWidth={1.5} />
|
||||
: null}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setSelectedItem(null)}
|
||||
className="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors cursor-pointer flex items-center justify-center"
|
||||
title="Close"
|
||||
aria-label="Close response view"
|
||||
>
|
||||
<IconX size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
</div>
|
||||
<ResponsePane item={selectedItem} collection={collection} />
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
) : (
|
||||
<div className="flex flex-1 w-[50%] overflow-y-auto">
|
||||
<div className="flex flex-col w-full h-full items-center justify-center text-center">
|
||||
<div className="mb-4 text-gray-400 dark:text-gray-500">
|
||||
<IconExternalLink size={64} strokeWidth={1.5} />
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-sm">
|
||||
Click on the status code to view the response
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import { test } from '../../playwright';
|
||||
import { setSandboxMode, runCollection, validateRunnerResults } from '../utils/page/index';
|
||||
|
||||
test.describe.parallel('Collection Run', () => {
|
||||
test('Run bruno-testbench in Developer Mode', async ({ pageWithUserData: page }) => {
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
|
||||
await page.getByText('bruno-testbench').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
// Set up developer mode
|
||||
await setSandboxMode(page, 'bruno-testbench', 'developer');
|
||||
|
||||
// Select environment
|
||||
await page.locator('.environment-selector').nth(1).click();
|
||||
await page.locator('.dropdown-item').getByText('Prod').click();
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const [totalRequests, passed, failed, skipped] = result
|
||||
.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/)
|
||||
.slice(1);
|
||||
// Run the collection
|
||||
await runCollection(page, 'bruno-testbench');
|
||||
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
failed: 0
|
||||
});
|
||||
});
|
||||
|
||||
test.fixme('Run bruno-testbench in Safe Mode', async ({ pageWithUserData: page }) => {
|
||||
@@ -32,18 +29,41 @@ test.describe.parallel('Collection Run', () => {
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.locator('.environment-selector').nth(1).click();
|
||||
await page.locator('.dropdown-item').getByText('Prod').click();
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
const collectionContainer = page.locator('.collection-name').filter({ hasText: 'bruno-testbench' });
|
||||
await collectionContainer.locator('.collection-actions').hover();
|
||||
await collectionContainer.locator('.collection-actions .icon').waitFor({ state: 'visible' });
|
||||
await collectionContainer.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
// Wait for the runner tab to open
|
||||
// If there are existing results, reset first, otherwise wait for Run Collection button
|
||||
const resetButton = page.getByRole('button', { name: 'Reset' });
|
||||
const runCollectionButton = page.getByRole('button', { name: 'Run Collection' });
|
||||
|
||||
// Check if Reset button is visible (means there are existing results)
|
||||
const resetVisible = await resetButton.isVisible().catch(() => false);
|
||||
if (resetVisible) {
|
||||
await resetButton.click();
|
||||
// Wait a bit for the reset to complete
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
// Now wait for and click Run Collection button
|
||||
await runCollectionButton.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await runCollectionButton.click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const [totalRequests, passed, failed, skipped] = result
|
||||
.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/)
|
||||
.slice(1);
|
||||
// Parse and validate test results from filter buttons
|
||||
const allButton = page.locator('button').filter({ hasText: /^All/ });
|
||||
const passedButton = page.locator('button').filter({ hasText: /^Passed/ });
|
||||
const failedButton = page.locator('button').filter({ hasText: /^Failed/ });
|
||||
const skippedButton = page.locator('button').filter({ hasText: /^Skipped/ });
|
||||
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
const totalRequests = parseInt(await allButton.locator('span').innerText());
|
||||
const passed = parseInt(await passedButton.locator('span').innerText());
|
||||
const failed = parseInt(await failedButton.locator('span').innerText());
|
||||
const skipped = parseInt(await skippedButton.locator('span').innerText());
|
||||
|
||||
await expect(failed).toBe(0);
|
||||
await expect(passed).toBe(totalRequests - skipped - failed);
|
||||
});
|
||||
});
|
||||
@@ -1,64 +1,40 @@
|
||||
import { test, expect } from '../../../../playwright';
|
||||
import { test } from '../../../../playwright';
|
||||
import { setSandboxMode, runCollection, validateRunnerResults } from '../../../utils/page';
|
||||
|
||||
test.describe.serial('jwt collection success', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
// init dev mode
|
||||
await page.getByTitle('jsonwebtoken').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
|
||||
// Set up developer mode
|
||||
await setSandboxMode(page, 'jsonwebtoken', 'developer');
|
||||
|
||||
// Run the collection
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
await runCollection(page, 'jsonwebtoken');
|
||||
|
||||
// Parse and validate test results
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(7);
|
||||
await expect(parseInt(passed)).toBe(7);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 7,
|
||||
passed: 7,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
// init safe mode
|
||||
await page.getByTitle('jsonwebtoken').click();
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
|
||||
// Set up safe mode
|
||||
await setSandboxMode(page, 'jsonwebtoken', 'safe');
|
||||
|
||||
// Run the collection
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
await runCollection(page, 'jsonwebtoken');
|
||||
|
||||
// Parse and validate test results
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(7);
|
||||
await expect(parseInt(passed)).toBe(7);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 7,
|
||||
passed: 7,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,59 +1,40 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
import { test } from '../../../../../playwright';
|
||||
import { setSandboxMode, runCollection, validateRunnerResults } from '../../../../utils/page';
|
||||
|
||||
test.describe.serial('basic ssl success', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('badssl').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up developer mode
|
||||
await setSandboxMode(page, 'badssl', 'developer');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'badssl');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
// Set up safe mode
|
||||
await setSandboxMode(page, 'badssl', 'safe');
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Run the collection
|
||||
await runCollection(page, 'badssl');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,57 +1,40 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
import { test } from '../../../../../playwright';
|
||||
import { setSandboxMode, runCollection, validateRunnerResults } from '../../../../utils/page';
|
||||
|
||||
test.describe.serial('self signed rejected', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
// init dev mode
|
||||
await page.getByText('self-signed-badssl').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(0);
|
||||
await expect(parseInt(failed)).toBe(1);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up developer mode
|
||||
await setSandboxMode(page, 'self-signed-badssl', 'developer');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'self-signed-badssl');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 0,
|
||||
failed: 1,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
// Set up safe mode
|
||||
await setSandboxMode(page, 'self-signed-badssl', 'safe');
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(0);
|
||||
await expect(parseInt(failed)).toBe(1);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Run the collection
|
||||
await runCollection(page, 'self-signed-badssl');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 0,
|
||||
failed: 1,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,59 +1,40 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
import { test } from '../../../../../playwright';
|
||||
import { setSandboxMode, runCollection, validateRunnerResults } from '../../../../utils/page';
|
||||
|
||||
test.describe.serial('self signed success with validation disabled', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('self-signed-badssl').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up developer mode
|
||||
await setSandboxMode(page, 'self-signed-badssl', 'developer');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'self-signed-badssl');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
// Set up safe mode
|
||||
await setSandboxMode(page, 'self-signed-badssl', 'safe');
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Run the collection
|
||||
await runCollection(page, 'self-signed-badssl');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,55 +1,40 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
import { test } from '../../../../../playwright';
|
||||
import { setSandboxMode, runCollection, validateRunnerResults } from '../../../../utils/page';
|
||||
|
||||
test.describe.serial('custom invalid ca cert added to the config and keep default ca certs', () => {
|
||||
test.describe('custom invalid ca cert added to the config and keep default ca certs', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
// init dev mode
|
||||
await page.getByText('custom-ca-certs').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up developer mode
|
||||
await setSandboxMode(page, 'custom-ca-certs', 'developer');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'custom-ca-certs');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up safe mode
|
||||
await setSandboxMode(page, 'custom-ca-certs', 'safe');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'custom-ca-certs');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,57 +1,40 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
import { test } from '../../../../../playwright';
|
||||
import { setSandboxMode, runCollection, validateRunnerResults } from '../../../../utils/page';
|
||||
|
||||
test.describe.serial('custom invalid ca cert added to the config and NO default ca certs', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('custom-ca-certs').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(0);
|
||||
await expect(parseInt(failed)).toBe(1);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up developer mode
|
||||
await setSandboxMode(page, 'custom-ca-certs', 'developer');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'custom-ca-certs');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 0,
|
||||
failed: 1,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(0);
|
||||
await expect(parseInt(failed)).toBe(1);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up safe mode
|
||||
await setSandboxMode(page, 'custom-ca-certs', 'safe');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'custom-ca-certs');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 0,
|
||||
failed: 1,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,57 +1,40 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
import { test } from '../../../../../playwright';
|
||||
import { setSandboxMode, runCollection, validateRunnerResults } from '../../../../utils/page';
|
||||
|
||||
test.describe.serial('custom valid ca cert added to the config and keep default ca certs', () => {
|
||||
test.describe('custom valid ca cert added to the config and keep default ca certs', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('custom-ca-certs').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up developer mode
|
||||
await setSandboxMode(page, 'custom-ca-certs', 'developer');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'custom-ca-certs');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up safe mode
|
||||
await setSandboxMode(page, 'custom-ca-certs', 'safe');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'custom-ca-certs');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,57 +1,40 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
import { test } from '../../../../../playwright';
|
||||
import { setSandboxMode, runCollection, validateRunnerResults } from '../../../../utils/page';
|
||||
|
||||
test.describe.serial('custom valid ca cert added to the config and NO default ca certs', () => {
|
||||
test.describe('custom valid ca cert added to the config and NO default ca certs', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('custom-ca-certs').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up developer mode
|
||||
await setSandboxMode(page, 'custom-ca-certs', 'developer');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'custom-ca-certs');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
// Set up safe mode
|
||||
await setSandboxMode(page, 'custom-ca-certs', 'safe');
|
||||
|
||||
// Run the collection
|
||||
await runCollection(page, 'custom-ca-certs');
|
||||
|
||||
// Validate test results
|
||||
await validateRunnerResults(page, {
|
||||
totalRequests: 1,
|
||||
passed: 1,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1 +1,2 @@
|
||||
export * from './actions';
|
||||
export * from './runner';
|
||||
|
||||
191
tests/utils/page/runner.ts
Normal file
191
tests/utils/page/runner.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
import { Page, expect } from '../../../playwright';
|
||||
|
||||
/**
|
||||
* Reads test result counts from the filter buttons in the runner results view
|
||||
* @param page - The Playwright page object
|
||||
* @returns An object with totalRequests, passed, failed, and skipped counts
|
||||
*/
|
||||
export const getRunnerResultCounts = async (page: Page) => {
|
||||
const allButton = page.locator('button').filter({ hasText: /^All/ });
|
||||
const passedButton = page.locator('button').filter({ hasText: /^Passed/ });
|
||||
const failedButton = page.locator('button').filter({ hasText: /^Failed/ });
|
||||
const skippedButton = page.locator('button').filter({ hasText: /^Skipped/ });
|
||||
|
||||
const totalRequests = parseInt(await allButton.locator('span').innerText());
|
||||
const passed = parseInt(await passedButton.locator('span').innerText());
|
||||
const failed = parseInt(await failedButton.locator('span').innerText());
|
||||
const skipped = parseInt(await skippedButton.locator('span').innerText());
|
||||
|
||||
return { totalRequests, passed, failed, skipped };
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs a collection by clicking the Run menu item and handling the runner tab
|
||||
* Includes logic to reset existing results if present
|
||||
* @param page - The Playwright page object
|
||||
* @param collectionName - The name of the collection to run
|
||||
* @returns void
|
||||
*/
|
||||
export const runCollection = async (page: Page, collectionName: string) => {
|
||||
// Ensure collection is visible and loaded
|
||||
const collectionContainer = page.locator('.collection-name').filter({ hasText: collectionName });
|
||||
await collectionContainer.waitFor({ state: 'visible' });
|
||||
// Wait a bit for the UI to stabilize
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Open collection actions menu
|
||||
await collectionContainer.locator('.collection-actions').hover();
|
||||
const icon = collectionContainer.locator('.collection-actions .icon');
|
||||
await icon.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await page.waitForTimeout(200); // Small delay to ensure hover state is stable
|
||||
await icon.click();
|
||||
|
||||
// Click Run menu item
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
|
||||
// Handle runner tab - reset if needed, then run
|
||||
const resetButton = page.getByRole('button', { name: 'Reset' });
|
||||
const runCollectionButton = page.getByRole('button', { name: 'Run Collection' });
|
||||
|
||||
// Check if Reset button is visible (means there are existing results)
|
||||
const resetVisible = await resetButton.isVisible().catch(() => false);
|
||||
if (resetVisible) {
|
||||
await resetButton.click();
|
||||
// Wait a bit for the reset to complete
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
// Now wait for and click Run Collection button
|
||||
await runCollectionButton.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await runCollectionButton.click();
|
||||
|
||||
// Wait for the run to complete
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets up the JavaScript sandbox mode for a collection
|
||||
* @param page - The Playwright page object
|
||||
* @param collectionName - The name of the collection (can be title or text)
|
||||
* @param mode - 'developer' or 'safe' mode
|
||||
* @returns void
|
||||
*/
|
||||
export const setSandboxMode = async (page: Page, collectionName: string, mode: 'developer' | 'safe') => {
|
||||
// Click on the collection name - try sidebar first, then fall back to collection tab/name
|
||||
// First try sidebar collection name (more reliable)
|
||||
const sidebarCollection = page.locator('#sidebar-collection-name').filter({ hasText: collectionName });
|
||||
const sidebarExists = await sidebarCollection.count().then((count) => count > 0).catch(() => false);
|
||||
|
||||
if (sidebarExists) {
|
||||
await sidebarCollection.click();
|
||||
} else {
|
||||
// Fall back to collection by title or text
|
||||
const collectionByTitle = page.getByTitle(collectionName);
|
||||
const collectionByText = page.getByText(collectionName);
|
||||
const titleExists = await collectionByTitle.count().then((count) => count > 0).catch(() => false);
|
||||
if (titleExists) {
|
||||
await collectionByTitle.click();
|
||||
} else {
|
||||
await collectionByText.click();
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a moment for the UI to load
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Check if there's already a mode selected - if so, we need to click the badge to open settings tab
|
||||
// Look for the Developer Mode or Safe Mode badge/button
|
||||
const developerModeBadge = page.locator('.developer-mode').filter({ hasText: 'Developer Mode' });
|
||||
const safeModeBadge = page.locator('.safe-mode').filter({ hasText: 'Safe Mode' });
|
||||
|
||||
const developerBadgeExists = await developerModeBadge.count().then((count) => count > 0).catch(() => false);
|
||||
const safeBadgeExists = await safeModeBadge.count().then((count) => count > 0).catch(() => false);
|
||||
|
||||
// If a badge exists, click it to open the security settings tab
|
||||
if (developerBadgeExists || safeBadgeExists) {
|
||||
// Click the appropriate badge to open the security settings tab
|
||||
if (developerBadgeExists) {
|
||||
await developerModeBadge.click();
|
||||
} else {
|
||||
await safeModeBadge.click();
|
||||
}
|
||||
|
||||
// Wait for the security settings tab to be active and form to be visible
|
||||
// Look for the security settings content - it should have "JavaScript Sandbox" heading
|
||||
await page.getByText('JavaScript Sandbox').waitFor({ state: 'visible', timeout: 10000 });
|
||||
await page.waitForTimeout(300);
|
||||
}
|
||||
// If no badge exists, the modal should have appeared automatically (first time selection)
|
||||
|
||||
// Wait for security settings form to be visible - wait for either radio button
|
||||
// These should be in the active tab (either modal or tab)
|
||||
const safeModeRadio = page.getByLabel('Safe Mode');
|
||||
const developerRadio = page.getByLabel('Developer Mode(use only if');
|
||||
|
||||
// Wait for at least one of them to be visible
|
||||
await Promise.race([
|
||||
safeModeRadio.waitFor({ state: 'visible', timeout: 10000 }).catch(() => {}),
|
||||
developerRadio.waitFor({ state: 'visible', timeout: 10000 }).catch(() => {})
|
||||
]);
|
||||
|
||||
// Additional wait to ensure UI is stable
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
if (mode === 'developer') {
|
||||
await developerRadio.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await developerRadio.check();
|
||||
} else {
|
||||
// For safe mode, check if developer mode is currently selected
|
||||
const developerModeChecked = await developerRadio.isChecked().catch(() => false);
|
||||
|
||||
if (developerModeChecked) {
|
||||
// Click the Developer Mode label text inside the security settings form
|
||||
// Scope to the form container to avoid clicking the badge
|
||||
// The form should have the "JavaScript Sandbox" heading, so scope to that container
|
||||
const securityForm = page.locator('div').filter({ hasText: 'JavaScript Sandbox' }).locator('..').first();
|
||||
const developerLabel = securityForm.locator('label').filter({ hasText: /^Developer Mode/ }).first();
|
||||
await developerLabel.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await developerLabel.click();
|
||||
// Wait for UI to update
|
||||
await page.waitForTimeout(300);
|
||||
}
|
||||
|
||||
// Ensure Safe Mode radio is visible and check it
|
||||
await safeModeRadio.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await safeModeRadio.check();
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates runner results against expected counts
|
||||
* @param page - The Playwright page object
|
||||
* @param expected - Expected counts
|
||||
* @returns void
|
||||
*/
|
||||
export const validateRunnerResults = async (page: Page,
|
||||
expected: {
|
||||
totalRequests?: number;
|
||||
passed?: number;
|
||||
failed?: number;
|
||||
skipped?: number;
|
||||
}) => {
|
||||
const { totalRequests, passed, failed, skipped } = await getRunnerResultCounts(page);
|
||||
|
||||
if (expected.totalRequests !== undefined) {
|
||||
await expect(totalRequests).toBe(expected.totalRequests);
|
||||
}
|
||||
if (expected.passed !== undefined) {
|
||||
await expect(passed).toBe(expected.passed);
|
||||
}
|
||||
if (expected.failed !== undefined) {
|
||||
await expect(failed).toBe(expected.failed);
|
||||
}
|
||||
if (expected.skipped !== undefined) {
|
||||
await expect(skipped).toBe(expected.skipped);
|
||||
}
|
||||
|
||||
// Validate that passed + failed + skipped = totalRequests
|
||||
await expect(passed).toBe(totalRequests - skipped - failed);
|
||||
};
|
||||
Reference in New Issue
Block a user