Events for E2E testing (#3081)
* ui: change export event ui, add rightblock panel * ui: add timeline select checkbox * ui: keep selected framework in localstorage * ui: on timeline => on the timeline
This commit is contained in:
parent
9f57271af2
commit
ee46413b13
23 changed files with 765 additions and 405 deletions
File diff suppressed because one or more lines are too long
|
|
@ -4,4 +4,4 @@ enableGlobalCache: true
|
|||
|
||||
nodeLinker: pnpm
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.6.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.7.0.cjs
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ function Player(props: IProps) {
|
|||
|
||||
if (!playerContext.player) return null;
|
||||
|
||||
const maxWidth = activeTab ? 'calc(100vw - 270px)' : '100vw';
|
||||
const activeTabWidth = activeTab === 'EXPORT' ? 360 : 270
|
||||
const maxWidth = activeTab ? `calc(100vw - ${activeTabWidth}px)` : '100vw';
|
||||
|
||||
const handleResize = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import EventsBlock from '../Session_/EventsBlock';
|
||||
import HighlightPanel from "../Session_/Highlight/HighlightPanel";
|
||||
import PageInsightsPanel from '../Session_/PageInsightsPanel/PageInsightsPanel';
|
||||
import UnitStepsModal from "../Session_/UnitStepsModal";
|
||||
import TagWatch from 'Components/Session/Player/TagWatch';
|
||||
|
||||
import cn from 'classnames';
|
||||
|
|
@ -39,6 +40,12 @@ function RightBlock({
|
|||
<HighlightPanel onClose={() => setActiveTab('')} />
|
||||
</div>
|
||||
)
|
||||
case 'EXPORT':
|
||||
return (
|
||||
<div className={cn('bg-white border-l', stl.extraPanel)}>
|
||||
<UnitStepsModal onClose={() => setActiveTab('EVENTS')} />
|
||||
</div>
|
||||
)
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,3 +4,10 @@
|
|||
height: calc(100vh - 50px);
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.extraPanel {
|
||||
width: 360px;
|
||||
min-width: 360px;
|
||||
height: calc(100vh - 50px);
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,18 +24,6 @@ function EventSearch(props) {
|
|||
onChange={onChange}
|
||||
prefix={<SearchOutlined />}
|
||||
/>
|
||||
|
||||
<Tooltip title="Close Panel" placement='bottom' >
|
||||
<Button
|
||||
className="ml-2"
|
||||
type='text'
|
||||
onClick={() => {
|
||||
setActiveTab('');
|
||||
toggleEvents();
|
||||
}}
|
||||
icon={<CloseOutlined />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,20 +4,31 @@ import cn from 'classnames';
|
|||
import { observer } from 'mobx-react-lite';
|
||||
import React from 'react';
|
||||
import { VList, VListHandle } from 'virtua';
|
||||
|
||||
import { Button } from 'antd'
|
||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { Icon } from 'UI';
|
||||
|
||||
import { Search } from 'lucide-react'
|
||||
import EventGroupWrapper from './EventGroupWrapper';
|
||||
import EventSearch from './EventSearch/EventSearch';
|
||||
import styles from './eventsBlock.module.css';
|
||||
import { CloseOutlined } from ".store/@ant-design-icons-virtual-42686020c5/package";
|
||||
import { Tooltip } from ".store/antd-virtual-9dbfadb7f6/package";
|
||||
import { getDefaultFramework, frameworkIcons } from "../UnitStepsModal";
|
||||
|
||||
interface IProps {
|
||||
setActiveTab: (tab?: string) => void;
|
||||
}
|
||||
|
||||
const MODES = {
|
||||
SELECT: 'select',
|
||||
SEARCH: 'search',
|
||||
EXPORT: 'export',
|
||||
}
|
||||
|
||||
function EventsBlock(props: IProps) {
|
||||
const defaultFramework = getDefaultFramework();
|
||||
const [mode, setMode] = React.useState(MODES.SELECT);
|
||||
const { notesStore, uxtestingStore, uiPlayerStore, sessionStore } = useStore();
|
||||
const session = sessionStore.current;
|
||||
const notesWithEvents = session.notesWithEvents;
|
||||
|
|
@ -197,7 +208,12 @@ function EventsBlock(props: IProps) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className={cn(styles.header, 'py-4 px-2 bg-gradient-to-t from-transparent to-neutral-50 h-[57px]' )}>
|
||||
<div
|
||||
className={cn(
|
||||
styles.header,
|
||||
'py-4 px-2 bg-gradient-to-t from-transparent to-neutral-50 h-[57px]'
|
||||
)}
|
||||
>
|
||||
{uxtestingStore.isUxt() ? (
|
||||
<div style={{ width: 240, height: 130 }} className={'relative'}>
|
||||
<video
|
||||
|
|
@ -220,14 +236,47 @@ function EventsBlock(props: IProps) {
|
|||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className={cn(styles.hAndProgress, 'mt-0')}>
|
||||
{mode === MODES.SELECT ? (
|
||||
<div className={'flex items-center gap-2'}>
|
||||
<Button
|
||||
onClick={() => setActiveTab('EXPORT')}
|
||||
type={'default'}
|
||||
shape={'circle'}
|
||||
>
|
||||
<Icon name={frameworkIcons[defaultFramework]} size={18} />
|
||||
</Button>
|
||||
<Button
|
||||
className={'flex items-center gap-2'}
|
||||
onClick={() => setMode(MODES.SEARCH)}
|
||||
>
|
||||
<Search size={14} />
|
||||
<div>Search {usedEvents.length} events</div>
|
||||
</Button>
|
||||
<Tooltip title="Close Panel" placement='bottom' >
|
||||
<Button
|
||||
className="ml-auto"
|
||||
type='text'
|
||||
onClick={() => {
|
||||
setActiveTab('');
|
||||
}}
|
||||
icon={<CloseOutlined />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
{mode === MODES.SEARCH ?
|
||||
<div className={'flex items-center gap-2'}>
|
||||
<EventSearch
|
||||
onChange={write}
|
||||
setActiveTab={setActiveTab}
|
||||
value={query}
|
||||
eventsText={usedEvents.length ? `${usedEvents.length} Events` : '0 Events'}
|
||||
eventsText={
|
||||
usedEvents.length ? `${usedEvents.length} Events` : '0 Events'
|
||||
}
|
||||
/>
|
||||
<Button type={'text'} onClick={() => setMode(MODES.SELECT)}>Cancel</Button>
|
||||
</div>
|
||||
: null}
|
||||
</div>
|
||||
<div
|
||||
className={cn('flex-1 pb-4', styles.eventsList)}
|
||||
|
|
@ -248,9 +297,8 @@ function EventsBlock(props: IProps) {
|
|||
ref={scroller}
|
||||
>
|
||||
{usedEvents.map((_, i) => {
|
||||
return renderGroup({ index: i })
|
||||
}
|
||||
)}
|
||||
return renderGroup({ index: i });
|
||||
})}
|
||||
</VList>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ import { WebEventsList, MobEventsList } from './EventsList';
|
|||
import NotesList from './NotesList';
|
||||
import SkipIntervalsList from './SkipIntervalsList';
|
||||
import TimelineTracker from 'Components/Session_/Player/Controls/TimelineTracker';
|
||||
import { ZoomDragLayer, HighlightDragLayer } from 'Components/Session_/Player/Controls/components/ZoomDragLayer';
|
||||
import {
|
||||
ZoomDragLayer,
|
||||
HighlightDragLayer,
|
||||
ExportEventsSelection
|
||||
} from "Components/Session_/Player/Controls/components/ZoomDragLayer";
|
||||
|
||||
function Timeline({ isMobile }: { isMobile: boolean }) {
|
||||
const { player, store } = useContext(PlayerContext);
|
||||
|
|
@ -24,6 +28,7 @@ function Timeline({ isMobile }: { isMobile: boolean }) {
|
|||
const timezone = sessionStore.current.timezone;
|
||||
const issues = sessionStore.current.issues ?? [];
|
||||
const timelineZoomEnabled = uiPlayerStore.timelineZoom.enabled;
|
||||
const exportEventsEnabled = uiPlayerStore.exportEventsSelection.enabled;
|
||||
const highlightEnabled = uiPlayerStore.highlightSelection.enabled;
|
||||
const { playing, skipToIssue, ready, endTime, devtoolsLoading, domLoading } = store.get();
|
||||
|
||||
|
|
@ -135,6 +140,7 @@ function Timeline({ isMobile }: { isMobile: boolean }) {
|
|||
>
|
||||
{timelineZoomEnabled ? <ZoomDragLayer scale={scale} /> : null}
|
||||
{highlightEnabled ? <HighlightDragLayer scale={scale} /> : null}
|
||||
{exportEventsEnabled ? <ExportEventsSelection scale={scale} /> : null}
|
||||
<div
|
||||
className={stl.progress}
|
||||
onClick={ready ? jumpToTime : undefined}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,29 @@ export const ZoomDragLayer = observer(({ scale }: Props) => {
|
|||
);
|
||||
});
|
||||
|
||||
export const ExportEventsSelection = observer(({ scale }: Props) => {
|
||||
const { uiPlayerStore } = useStore();
|
||||
const toggleExportEventsSelection = uiPlayerStore.toggleExportEventsSelection;
|
||||
const timelineZoomStartTs = uiPlayerStore.exportEventsSelection.startTs;
|
||||
const timelineZoomEndTs = uiPlayerStore.exportEventsSelection.endTs;
|
||||
|
||||
const onDrag = (start: number, end: number) => {
|
||||
toggleExportEventsSelection({
|
||||
enabled: true,
|
||||
range: [start, end],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<DraggableMarkers
|
||||
scale={scale}
|
||||
onDragEnd={onDrag}
|
||||
defaultStartPos={timelineZoomStartTs}
|
||||
defaultEndPos={timelineZoomEndTs}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
function DraggableMarkers({
|
||||
scale,
|
||||
onDragEnd,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import { Bookmark as BookmarkIcn, BookmarkCheck, Vault } from 'lucide-react';
|
|||
import { useModal } from 'Components/ModalContext';
|
||||
import IssueForm from 'Components/Session_/Issues/IssueForm';
|
||||
import ShareModal from '../shared/SharePopup/SharePopup';
|
||||
import UnitStepsModal from "./UnitStepsModal";
|
||||
|
||||
const disableDevtools = 'or_devtools_uxt_toggle';
|
||||
|
||||
|
|
@ -117,13 +116,6 @@ function SubHeader(props) {
|
|||
});
|
||||
};
|
||||
|
||||
const exportEvents = () => {
|
||||
const allEvents = sessionStore.current.events;
|
||||
const width = store.get().width;
|
||||
const height = store.get().height;
|
||||
openModal(<UnitStepsModal width={width} height={height} events={allEvents} />, { title: 'Export Events', width: 640 });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
|
@ -196,14 +188,6 @@ function SubHeader(props) {
|
|||
</div>,
|
||||
onClick: showKbHelp
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
label: <div className={'flex items-center gap-2'}>
|
||||
<Bot size={16} strokeWidth={1} />
|
||||
<span>Export Events</span>
|
||||
</div>,
|
||||
onClick: exportEvents,
|
||||
}
|
||||
]
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,96 +1,214 @@
|
|||
import React from 'react';
|
||||
import { TYPES, Input, Click, Location } from 'App/types/session/event';
|
||||
import { CodeBlock, CopyButton } from 'UI';
|
||||
import { Segmented } from 'antd';
|
||||
import { TYPES } from 'App/types/session/event';
|
||||
import { CodeBlock, Icon } from 'UI';
|
||||
import { Select, Radio, Checkbox } from 'antd';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { PlayerContext } from 'Components/Session/playerContext';
|
||||
import { X } from 'lucide-react';
|
||||
import { puppeteerEvents, cypressEvents, playWrightEvents } from './utils';
|
||||
|
||||
interface Props {
|
||||
events: Input[] | Click[] | Location[];
|
||||
width: number;
|
||||
height: number;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function UnitStepsModal({ events, width, height }: Props) {
|
||||
const defaultFrameworkKey = '__$defaultFrameworkKey$__';
|
||||
export const getDefaultFramework = () => {
|
||||
const stored = localStorage.getItem(defaultFrameworkKey);
|
||||
return stored ?? 'cypress';
|
||||
}
|
||||
export const frameworkIcons = {
|
||||
cypress: 'cypress',
|
||||
puppeteer: 'puppeteer',
|
||||
playwright: 'pwright',
|
||||
}
|
||||
function UnitStepsModal({ onClose }: Props) {
|
||||
const { sessionStore, uiPlayerStore } = useStore();
|
||||
const { store, player } = React.useContext(PlayerContext);
|
||||
const [eventStr, setEventStr] = React.useState('');
|
||||
const [activeFramework, setActiveFramework] = React.useState('puppeteer');
|
||||
const [mode, setMode] = React.useState('events');
|
||||
const [activeFramework, setActiveFramework] = React.useState(getDefaultFramework);
|
||||
const events = React.useMemo(() => {
|
||||
if (!uiPlayerStore.exportEventsSelection.enabled) {
|
||||
return sessionStore.current.events;
|
||||
} else {
|
||||
return sessionStore.current.events.filter((ev) => {
|
||||
return (
|
||||
ev.time >= uiPlayerStore.exportEventsSelection.startTs &&
|
||||
ev.time <= uiPlayerStore.exportEventsSelection.endTs
|
||||
);
|
||||
});
|
||||
}
|
||||
}, [
|
||||
sessionStore.current.events,
|
||||
uiPlayerStore.exportEventsSelection.enabled,
|
||||
uiPlayerStore.exportEventsSelection.startTs,
|
||||
uiPlayerStore.exportEventsSelection.endTs,
|
||||
]);
|
||||
const { tabNames, currentTab } = store.get();
|
||||
|
||||
React.useEffect(() => {
|
||||
player.pause();
|
||||
return () => {
|
||||
uiPlayerStore.toggleExportEventsSelection({ enabled: false });
|
||||
};
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const userEventTypes = [TYPES.LOCATION, TYPES.CLICK, TYPES.INPUT];
|
||||
const puppeteerEvents = {
|
||||
[TYPES.LOCATION]: (event: Location) => `await page.goto('${event.url}')`,
|
||||
[TYPES.CLICK]: (event: Click) =>
|
||||
`await page.locator('${
|
||||
event.selector.length ? event.selector : event.label
|
||||
}').click()`,
|
||||
[TYPES.INPUT]: (event: Input) =>
|
||||
`await page.locator('${event.label}').type('Test Input')`,
|
||||
screen: () =>
|
||||
`await page.setViewport({width: ${width}, height: ${height})`,
|
||||
};
|
||||
const cypressEvents = {
|
||||
[TYPES.LOCATION]: (event: Location) => `cy.visit('${event.url}')`,
|
||||
[TYPES.CLICK]: (event: Click) =>
|
||||
`cy.get('${
|
||||
event.selector.length ? event.selector : event.label
|
||||
}').click()`,
|
||||
[TYPES.INPUT]: (event: Input) =>
|
||||
`cy.get('${event.label}').type('Test Input')`,
|
||||
screen: () => `cy.viewport(${width}, ${height})`,
|
||||
};
|
||||
const playWrightEvents = {
|
||||
[TYPES.LOCATION]: (event: Location) => `await page.goto('${event.url}')`,
|
||||
[TYPES.CLICK]: (event: Click) =>
|
||||
event.selector.length
|
||||
? `await page.locator('${event.selector}').click()`
|
||||
: `await page.getByText('${event.label}').click()`,
|
||||
[TYPES.INPUT]: (event: Input) =>
|
||||
`await page.getByLabel('${event.label}').fill('Test Input')`,
|
||||
screen: () =>
|
||||
`await page.setViewport({width: ${width}, height: ${height})`,
|
||||
};
|
||||
|
||||
const collections = {
|
||||
puppeteer: puppeteerEvents,
|
||||
cypress: cypressEvents,
|
||||
playwright: playWrightEvents,
|
||||
}
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const usedCollection = collections[activeFramework];
|
||||
|
||||
let finalScript = '';
|
||||
if (mode === 'test') {
|
||||
const pageName = tabNames[currentTab] ?? 'Test Name';
|
||||
const firstUrl =
|
||||
events.find((ev) => ev.type === TYPES.LOCATION)?.url ?? 'page';
|
||||
finalScript += usedCollection.testIntro(pageName, firstUrl);
|
||||
finalScript += '\n';
|
||||
}
|
||||
events.forEach((ev) => {
|
||||
if (userEventTypes.includes(ev.type)) {
|
||||
if (mode === 'test') {
|
||||
finalScript += ' ';
|
||||
}
|
||||
finalScript += usedCollection[ev.type](ev);
|
||||
finalScript += '\n';
|
||||
}
|
||||
});
|
||||
if (mode === 'test') {
|
||||
finalScript += usedCollection.testOutro();
|
||||
}
|
||||
setEventStr(finalScript);
|
||||
}, [events, activeFramework]);
|
||||
}, [events, activeFramework, mode]);
|
||||
|
||||
const enableZoom = () => {
|
||||
const time = store.get().time;
|
||||
const endTime = store.get().endTime;
|
||||
const closestEvent = sessionStore.current.events.reduce((prev, curr) => {
|
||||
return Math.abs(curr.time - time) < Math.abs(prev.time - time)
|
||||
? curr
|
||||
: prev;
|
||||
});
|
||||
const closestInd = sessionStore.current.events.indexOf(closestEvent);
|
||||
if (closestEvent) {
|
||||
const beforeCenter = closestInd > 4 ? closestInd - 4 : null;
|
||||
const afterCenter =
|
||||
closestInd < sessionStore.current.events.length - 4
|
||||
? closestInd + 4
|
||||
: null;
|
||||
|
||||
uiPlayerStore.toggleExportEventsSelection({
|
||||
enabled: true,
|
||||
range: [
|
||||
beforeCenter ? sessionStore.current.events[beforeCenter].time : 0,
|
||||
afterCenter ? sessionStore.current.events[afterCenter].time : endTime,
|
||||
],
|
||||
});
|
||||
} else {
|
||||
const distance = Math.max(endTime / 40, 2500);
|
||||
|
||||
uiPlayerStore.toggleExportEventsSelection({
|
||||
enabled: true,
|
||||
range: [
|
||||
Math.max(time - distance, 0),
|
||||
Math.min(time + distance, endTime),
|
||||
],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const toggleZoom = (enabled?: boolean) => {
|
||||
if (enabled) {
|
||||
enableZoom();
|
||||
} else {
|
||||
uiPlayerStore.toggleExportEventsSelection({ enabled: false });
|
||||
}
|
||||
};
|
||||
|
||||
const changeFramework = (framework: string) => {
|
||||
localStorage.setItem(defaultFrameworkKey, framework);
|
||||
setActiveFramework(framework);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'bg-white h-full flex flex-col items-start gap-2'}>
|
||||
<div className={'flex items-center gap-4'}>
|
||||
<Segmented
|
||||
<div
|
||||
className={'bg-white h-screen w-full flex flex-col items-start gap-2 p-4'}
|
||||
style={{ marginTop: -50 }}
|
||||
>
|
||||
<div className={'flex items-center justify-between w-full'}>
|
||||
<div className={'font-semibold text-xl'}>Copy Events</div>
|
||||
<div className={'cursor-pointer'} onClick={onClose}>
|
||||
<X size={18} />
|
||||
</div>
|
||||
</div>
|
||||
<Select
|
||||
className={'w-full'}
|
||||
options={[
|
||||
{ label: 'Puppeteer', value: 'puppeteer' },
|
||||
{ label: 'Cypress', value: 'cypress' },
|
||||
{ label: 'Playwright', value: 'playwright' },
|
||||
{
|
||||
label: (
|
||||
<div className={'flex items-center gap-2'}>
|
||||
<Icon name={'cypress'} size={18} />
|
||||
<div>Cypress</div>
|
||||
</div>
|
||||
),
|
||||
value: 'cypress',
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<div className={'flex items-center gap-2'}>
|
||||
<Icon name={'puppeteer'} size={18} />
|
||||
<div>Puppeteer</div>
|
||||
</div>
|
||||
),
|
||||
value: 'puppeteer',
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<div className={'flex items-center gap-2'}>
|
||||
<Icon name={'pwright'} size={18} />
|
||||
<div>Playwright</div>
|
||||
</div>
|
||||
),
|
||||
value: 'playwright',
|
||||
},
|
||||
]}
|
||||
value={activeFramework}
|
||||
onChange={(value) => setActiveFramework(value)}
|
||||
onChange={changeFramework}
|
||||
/>
|
||||
<CopyButton
|
||||
size={'middle'}
|
||||
variant={'default'}
|
||||
content={eventStr}
|
||||
className={'capitalize font-medium text-neutral-400'}
|
||||
/>
|
||||
</div>
|
||||
<Radio.Group
|
||||
value={mode}
|
||||
onChange={(e) => setMode(e.target.value)}
|
||||
className={'w-full'}
|
||||
>
|
||||
<Radio value={'events'}>Events Only</Radio>
|
||||
<Radio value={'test'}>Complete Test</Radio>
|
||||
</Radio.Group>
|
||||
<Checkbox
|
||||
value={uiPlayerStore.exportEventsSelection.enabled}
|
||||
onChange={(e) => toggleZoom(e.target.checked)}
|
||||
>
|
||||
Select events on the timeline
|
||||
</Checkbox>
|
||||
<div className={'w-full'}>
|
||||
<CodeBlock code={eventStr} language={'javascript'} />
|
||||
<CodeBlock
|
||||
width={340}
|
||||
height={'calc(100vh - 146px)'}
|
||||
extra={`${events.length} Events`}
|
||||
copy
|
||||
code={eventStr}
|
||||
language={'javascript'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UnitStepsModal;
|
||||
export default observer(UnitStepsModal);
|
||||
|
|
|
|||
40
frontend/app/components/Session_/UnitStepsModal/utils.ts
Normal file
40
frontend/app/components/Session_/UnitStepsModal/utils.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { Click, Input, Location, TYPES } from "App/types/session/event";
|
||||
|
||||
export const puppeteerEvents = {
|
||||
[TYPES.LOCATION]: (event: Location) => `await page.goto('${event.url}')`,
|
||||
[TYPES.CLICK]: (event: Click) =>
|
||||
`await page.locator('${
|
||||
event.selector.length ? event.selector : event.label
|
||||
}').click()`,
|
||||
[TYPES.INPUT]: (event: Input) =>
|
||||
`await page.locator('${event.label}').type('Test Input')`,
|
||||
screen: (width: number, height: number) =>
|
||||
`await page.setViewport({width: ${width}, height: ${height})`,
|
||||
testIntro: (pageTitle: string, firstUrl: string) => `describe('${pageTitle}', () => {\n it('Navigates through ${firstUrl}', async () => {`,
|
||||
testOutro: () => ` })\n})`,
|
||||
};
|
||||
export const cypressEvents = {
|
||||
[TYPES.LOCATION]: (event: Location) => `cy.visit('${event.url}')`,
|
||||
[TYPES.CLICK]: (event: Click) =>
|
||||
`cy.get('${
|
||||
event.selector.length ? event.selector : event.label
|
||||
}').click()`,
|
||||
[TYPES.INPUT]: (event: Input) =>
|
||||
`cy.get('${event.label}').type('Test Input')`,
|
||||
screen: (width: number, height: number) => `cy.viewport(${width}, ${height})`,
|
||||
testIntro: (pageTitle: string, firstUrl: string) => `describe('${pageTitle}', () => {\n it('Navigates through ${firstUrl}', () => {`,
|
||||
testOutro: () => ` })\n})`,
|
||||
};
|
||||
export const playWrightEvents = {
|
||||
[TYPES.LOCATION]: (event: Location) => `await page.goto('${event.url}')`,
|
||||
[TYPES.CLICK]: (event: Click) =>
|
||||
event.selector.length
|
||||
? `await page.locator('${event.selector}').click()`
|
||||
: `await page.getByText('${event.label}').click()`,
|
||||
[TYPES.INPUT]: (event: Input) =>
|
||||
`await page.getByLabel('${event.label}').fill('Test Input')`,
|
||||
screen: (width: number, height: number) =>
|
||||
`await page.setViewport({width: ${width}, height: ${height})`,
|
||||
testIntro: (pageTitle: string, firstUrl: string) => `test.describe('${pageTitle}', () => {\n test('Navigates through ${firstUrl}', async () => {`,
|
||||
testOutro: () => ` })\n})`,
|
||||
};
|
||||
|
|
@ -1,19 +1,52 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import copyFn from 'copy-to-clipboard';
|
||||
import { Files } from 'lucide-react';
|
||||
import { Tooltip } from 'antd';
|
||||
import cn from 'classnames';
|
||||
|
||||
export default function CodeBlock({ code, language = 'javascript' }) {
|
||||
export default function CodeBlock({
|
||||
code = '',
|
||||
extra = '',
|
||||
language = 'javascript',
|
||||
copy = false,
|
||||
width = undefined,
|
||||
height = undefined,
|
||||
}) {
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
if (window.Prism) {
|
||||
Prism.highlightAll();
|
||||
}
|
||||
}, 0)
|
||||
}, 0);
|
||||
}, [code, language]);
|
||||
|
||||
return (
|
||||
<pre className='rounded-lg'>
|
||||
<code className={`language-${language}`}>
|
||||
{code}
|
||||
</code>
|
||||
<div className={'relative'}>
|
||||
{extra || copy ? (
|
||||
<div className={'w-full flex items-center justify-between mb-2'}>
|
||||
{extra && <div className="text-sm text-disabled-text">{extra}</div>}
|
||||
{copy && (
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => copyFn(code)}
|
||||
>
|
||||
<Tooltip title="Copy code" placement={'bottomLeft'}>
|
||||
<Files size={14} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
<pre
|
||||
className={cn(
|
||||
'rounded-lg relative',
|
||||
width ? 'overflow-x-auto' : '',
|
||||
height ? 'overflow-y-auto' : '',
|
||||
)}
|
||||
style={{ maxWidth: width, maxHeight: height }}
|
||||
>
|
||||
<code className={`language-${language}`}>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
19
frontend/app/components/ui/Icons/cypress.tsx
Normal file
19
frontend/app/components/ui/Icons/cypress.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
/* Auto-generated, do not edit */
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
size?: number | string;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
fill?: string;
|
||||
}
|
||||
|
||||
function Cypress(props: Props) {
|
||||
const { size = 14, width = size, height = size, fill = '' } = props;
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" width={ `${ width }px` } height={ `${ height }px` } ><g><path d="M8 1.075c-.5 0-.973.064-1.24.112v.001a6.896 6.896 0 0 0-5.696 6.813c0 .651.09 1.29.269 1.91.022.083.052.172.08.258a6.966 6.966 0 0 0 6.584 4.756c.037 0 .146 0 .29-.007a1.283 1.283 0 0 0 1.128-.792l.274-.667 3.182-7.746h-1.044l-1.339 3.393L9.14 5.713H8.036l1.892 4.63L8.52 13.76a.312.312 0 0 1-.273.194c-.083.003-.165.006-.25.006A5.992 5.992 0 0 1 2.26 9.645a5.954 5.954 0 0 1-.23-1.644 5.936 5.936 0 0 1 4.973-5.875c.128-.021.513-.084.997-.084 2.553 0 4.78 1.58 5.624 3.959.032.083.056.169.085.254.172.563.261 1.15.261 1.746a5.925 5.925 0 0 1-4.228 5.7l.28.925a6.889 6.889 0 0 0 4.915-6.624c0-.895-.172-1.66-.376-2.25l-.075-.21h-.002A6.894 6.894 0 0 0 8 1.075Zm-2.06 4.55c-.695 0-1.258.222-1.723.68C3.753 6.757 3.52 7.33 3.52 8c0 .667.235 1.235.696 1.688.466.457 1.029.679 1.723.679.986 0 1.824-.552 2.19-1.44l.018-.047-.941-.32c-.098.264-.437.86-1.267.86-.39 0-.719-.135-.979-.404A1.406 1.406 0 0 1 4.563 8c0-.404.13-.74.397-1.025.261-.268.59-.405.98-.405.83 0 1.168.625 1.267.86l.94-.32-.019-.048c-.364-.888-1.203-1.439-2.189-1.439Z" fill="#000" stroke="#000" strokeWidth=".289"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default Cypress;
|
||||
|
|
@ -159,6 +159,7 @@ export { default as Credit_card_2_back } from './credit_card_2_back';
|
|||
export { default as Cross } from './cross';
|
||||
export { default as Cubes } from './cubes';
|
||||
export { default as Cursor_trash } from './cursor_trash';
|
||||
export { default as Cypress } from './cypress';
|
||||
export { default as Dash } from './dash';
|
||||
export { default as Dashboard_icn } from './dashboard_icn';
|
||||
export { default as Dashboards_circle_alert } from './dashboards_circle_alert';
|
||||
|
|
@ -417,8 +418,10 @@ export { default as Plus_circle } from './plus_circle';
|
|||
export { default as Plus } from './plus';
|
||||
export { default as Prev1 } from './prev1';
|
||||
export { default as Pulse } from './pulse';
|
||||
export { default as Puppeteer } from './puppeteer';
|
||||
export { default as Puzzle_piece } from './puzzle_piece';
|
||||
export { default as Puzzle } from './puzzle';
|
||||
export { default as Pwright } from './pwright';
|
||||
export { default as Question_circle } from './question_circle';
|
||||
export { default as Question_lg } from './question_lg';
|
||||
export { default as Quotes } from './quotes';
|
||||
|
|
|
|||
19
frontend/app/components/ui/Icons/puppeteer.tsx
Normal file
19
frontend/app/components/ui/Icons/puppeteer.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
/* Auto-generated, do not edit */
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
size?: number | string;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
fill?: string;
|
||||
}
|
||||
|
||||
function Puppeteer(props: Props) {
|
||||
const { size = 14, width = size, height = size, fill = '' } = props;
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" width={ `${ width }px` } height={ `${ height }px` } ><path d="m12.887 12.169 1.796-.867v-.867l-4.15-1.983 4.15-2.137V5.48l-1.734-.805L8.034 7.12 3.037 4.674l-1.61.867v.743l3.902 2.137-3.902 1.982v.899l1.672.867 4.956-2.54 4.832 2.54Z" fill="#00D8A2" stroke="#00D8A2" strokeWidth=".555"/><path d="M13.112 11.815v-.373l1.352-.701v.42l-1.352.654ZM1.536 10.731l1.306.75v.299l-1.306-.661v-.388Zm6.693-1.577 4.495 2.22v.447L8.229 9.52v-.365ZM3.23 11.44l4.61-2.286v.364L3.23 11.85v-.41Zm3.264-3.735L1.83 5.453l1.208-.614 4.997 2.59 4.912-2.589 1.251.64-4.549 2.29a.247.247 0 0 0 0 .442l4.503 2.254-1.167.604-4.95-2.536-5.038 2.588-1.15-.66 4.65-2.312a.248.248 0 0 0-.002-.445ZM1.594 6.216v-.41l4.35 2.17-.44.176-3.91-1.936Zm8.92 1.96-.416-.197 4.366-2.197v.411l-3.95 1.984Zm.438.216 3.837-1.927a.115.115 0 0 0 .064-.103V5.45a.114.114 0 0 0-.063-.102L13 4.43a.115.115 0 0 0-.104 0l-4.81 2.44a.115.115 0 0 1-.103 0l-4.895-2.44a.115.115 0 0 0-.103 0l-1.775.901a.115.115 0 0 0-.063.103v.926c0 .044.025.084.064.104l3.854 1.904-3.836 1.905a.115.115 0 0 0-.064.1l-.016.912a.115.115 0 0 0 .063.104l1.843.933a.115.115 0 0 0 .103 0L7.983 9.9a.115.115 0 0 1 .103 0l4.765 2.423a.115.115 0 0 0 .102.001l1.835-.889a.115.115 0 0 0 .065-.103v-.952a.115.115 0 0 0-.064-.103l-3.837-1.885Z" fill="#000"/></svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default Puppeteer;
|
||||
19
frontend/app/components/ui/Icons/pwright.tsx
Normal file
19
frontend/app/components/ui/Icons/pwright.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
/* Auto-generated, do not edit */
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
size?: number | string;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
fill?: string;
|
||||
}
|
||||
|
||||
function Pwright(props: Props) {
|
||||
const { size = 14, width = size, height = size, fill = '' } = props;
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" width={ `${ width }px` } height={ `${ height }px` } ><path d="M5.458 9.239c-.516.146-.854.403-1.077.659.214-.187.5-.358.885-.467.394-.112.73-.111 1.009-.058v-.217a2.225 2.225 0 0 0-.817.083Zm-1.1-1.827-1.914.504s.035.05.1.115l1.622-.428s-.023.297-.223.562c.378-.286.415-.753.415-.753ZM5.96 11.91c-2.694.725-4.119-2.397-4.55-4.017-.2-.748-.287-1.315-.31-1.68a.62.62 0 0 1 .001-.1c-.14.009-.206.082-.193.292.024.365.11.931.31 1.68.431 1.62 1.857 4.742 4.55 4.016a2.735 2.735 0 0 0 1.358-.813 2.791 2.791 0 0 1-1.166.622Zm.506-6.41v.192h1.057L7.458 5.5h-.992Z" fill="#2D4552"/><path d="M7.76 7.08c.474.135.726.468.859.763l.53.15s-.073-1.031-1.006-1.296c-.873-.249-1.41.485-1.476.58.254-.181.625-.33 1.092-.197Zm4.217.768c-.874-.25-1.411.485-1.476.579.255-.18.625-.329 1.093-.196.474.135.725.468.858.763l.53.151s-.072-1.032-1.005-1.297Zm-.527 2.72L7.043 9.337s.048.242.23.555l3.712 1.038c.305-.177.466-.36.466-.36Zm-3.055 2.653c-3.49-.936-3.068-5.383-2.504-7.49.233-.868.472-1.513.67-1.946-.118-.024-.216.038-.313.235-.21.427-.48 1.122-.74 2.095-.565 2.107-.986 6.553 2.503 7.489 1.645.44 2.927-.23 3.882-1.28-.907.82-2.064 1.28-3.498.897Z" fill="#2D4552"/><path d="M6.466 10.869V9.97l-2.493.707s.185-1.07 1.485-1.439c.394-.112.73-.111 1.008-.057v-3.68h1.249a7.477 7.477 0 0 0-.378-.969c-.183-.372-.37-.125-.795.23-.3.25-1.056.784-2.195 1.091-1.14.307-2.06.226-2.444.16-.545-.095-.83-.214-.803.2.023.365.11.932.31 1.68.431 1.62 1.857 4.742 4.55 4.016.704-.19 1.2-.564 1.545-1.042H6.466ZM2.443 7.916l1.915-.504s-.056.736-.774.925c-.718.19-1.14-.42-1.14-.42Z" fill="#E2574C"/><path d="M13.671 5.544c-.497.087-1.691.196-3.167-.2C9.03 4.95 8.05 4.258 7.661 3.933c-.55-.46-.791-.781-1.03-.297-.21.427-.48 1.122-.74 2.095-.564 2.107-.986 6.554 2.504 7.49 3.489.934 5.346-3.127 5.91-5.235.261-.973.376-1.71.407-2.184.036-.538-.334-.382-1.04-.258ZM6.66 7.287s.55-.855 1.483-.59 1.005 1.297 1.005 1.297L6.66 7.287Zm2.277 3.838c-1.64-.48-1.894-1.789-1.894-1.789l4.407 1.233s-.89 1.03-2.513.556Zm1.558-2.688s.55-.855 1.482-.59c.932.266 1.006 1.298 1.006 1.298l-2.488-.708Z" fill="#2EAD33"/><path d="m5.678 10.195-.435.123c.103.58.284 1.135.568 1.627.05-.011.099-.02.149-.035a3.15 3.15 0 0 0 .373-.127 4.606 4.606 0 0 1-.655-1.588Zm-.17-4.08c-.223.834-.423 2.035-.368 3.24.098-.044.202-.083.318-.116l.08-.018c-.098-1.287.114-2.598.353-3.49.061-.225.122-.435.182-.63a5.646 5.646 0 0 1-.322.19c-.08.25-.162.522-.243.824Z" fill="#C04B41"/></svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default Pwright;
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -70,6 +70,11 @@ export default class UiPlayerStore {
|
|||
startTs: 0,
|
||||
endTs: 0,
|
||||
}
|
||||
exportEventsSelection = {
|
||||
enabled: false,
|
||||
startTs: 0,
|
||||
endTs: 0,
|
||||
}
|
||||
zoomTab: 'overview' | 'journey' | 'issues' | 'errors' = 'overview'
|
||||
dataSource: 'all' | 'current' = 'all'
|
||||
|
||||
|
|
@ -124,6 +129,12 @@ export default class UiPlayerStore {
|
|||
this.highlightSelection.endTs = payload.range?.[1] ?? 0;
|
||||
}
|
||||
|
||||
toggleExportEventsSelection = (payload: ToggleZoomPayload) => {
|
||||
this.exportEventsSelection.enabled = payload.enabled;
|
||||
this.exportEventsSelection.startTs = payload.range?.[0] ?? 0;
|
||||
this.exportEventsSelection.endTs = payload.range?.[1] ?? 0;
|
||||
}
|
||||
|
||||
setZoomTab = (tab: 'overview' | 'journey' | 'issues' | 'errors') => {
|
||||
this.zoomTab = tab;
|
||||
}
|
||||
|
|
|
|||
10
frontend/app/svg/icons/cypress.svg
Normal file
10
frontend/app/svg/icons/cypress.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1_1027)">
|
||||
<path d="M7.99916 1.07536C7.49976 1.07536 7.02694 1.13894 6.7599 1.18692V1.18808C5.16004 1.47078 3.71137 2.30928 2.66933 3.55573C1.62729 4.80218 1.05881 6.37652 1.06412 8.00115C1.06412 8.652 1.15487 9.29186 1.3329 9.91034C1.35487 9.99415 1.3855 10.0832 1.41209 10.1687C2.34096 12.9808 5.00966 14.9235 7.99685 14.9246C8.03442 14.9246 8.14251 14.9246 8.28701 14.9183C8.53083 14.9072 8.76644 14.8268 8.96618 14.6865C9.16593 14.5463 9.32152 14.352 9.41472 14.1264L9.6887 13.4594L12.8707 5.71279H11.8268L10.4881 9.10632L9.13901 5.71279H8.03558L9.928 10.3439L8.51995 13.7599C8.49763 13.8149 8.46011 13.8623 8.41179 13.8967C8.36348 13.9311 8.30636 13.951 8.24713 13.9541C8.1639 13.957 8.0824 13.9599 7.99743 13.9599C5.34722 13.9599 2.98776 12.1854 2.25888 9.64503C2.10581 9.1105 2.02838 8.55717 2.02883 8.00115C2.02469 6.5908 2.52282 5.22505 3.43399 4.14855C4.34517 3.07204 5.60985 2.35512 7.00151 2.1262C7.12983 2.10481 7.51536 2.04238 7.99858 2.04238C10.5517 2.04238 12.7782 3.62152 13.6227 6.00064C13.655 6.08387 13.6793 6.17 13.7076 6.25497C13.8805 6.81795 13.9695 7.40406 13.9695 8.00115C13.9745 9.28042 13.5652 10.5269 12.803 11.5543C12.0408 12.5817 10.9665 13.3348 9.74072 13.701L10.0216 14.6258C12.9609 13.7322 14.9348 11.0698 14.9359 8.00173C14.9359 7.10696 14.7637 6.34282 14.5602 5.75209L14.4845 5.54227H14.4833C13.4666 2.85276 10.9158 1.07536 7.99916 1.07536ZM5.93911 5.62435C5.24549 5.62435 4.68193 5.84746 4.21662 6.3041C3.75306 6.75842 3.51954 7.3295 3.51954 8.00115C3.51954 8.66818 3.75479 9.23637 4.21605 9.68896C4.68193 10.1456 5.24491 10.3676 5.93853 10.3676C6.92463 10.3676 7.76275 9.81555 8.12806 8.92887L8.14713 8.88147L7.20555 8.56125C7.10844 8.82483 6.76914 9.42134 5.93911 9.42134C5.54895 9.42134 5.22006 9.28608 4.95995 9.01673C4.6958 8.74506 4.56286 8.40287 4.56286 8.00115C4.56286 7.59654 4.69291 7.26129 4.95995 6.97633C5.22122 6.70755 5.54953 6.57056 5.93911 6.57056C6.7703 6.57056 7.10844 7.19598 7.2067 7.43123L8.14713 7.11159L8.12806 7.06303C7.76391 6.1752 6.92463 5.62435 5.93911 5.62435Z" fill="black" stroke="black" stroke-width="0.289008"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1_1027">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
4
frontend/app/svg/icons/puppeteer.svg
Normal file
4
frontend/app/svg/icons/puppeteer.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.8868 12.169L14.6833 11.3017V10.4345L10.5329 8.4522L14.6833 6.31509V5.47885L12.9488 4.67353L8.03444 7.12036L3.03747 4.67353L1.42688 5.5408V6.28412L5.32944 8.42123L1.42688 10.4035V11.3017L3.09941 12.169L8.05507 9.62919L12.8868 12.169Z" fill="#00D8A2" stroke="#00D8A2" stroke-width="0.555154"/>
|
||||
<path d="M13.1121 11.8153V11.4417L14.4644 10.741V11.1606L13.1121 11.8153ZM1.53571 10.7312L2.84237 11.4813V11.7799L1.53571 11.1189V10.7312ZM8.22871 9.15374L12.7237 11.3747V11.8212L8.22871 9.51851V9.15374ZM3.2308 11.4396L7.84033 9.15415V9.51839L3.2308 11.8505V11.4396ZM6.49464 7.7052L1.83006 5.45314L3.03819 4.83914L8.03452 7.42926L12.9472 4.84025L14.198 5.4809L9.6492 7.77012C9.46729 7.86164 9.46758 8.12133 9.64961 8.21249L14.1515 10.4657L12.9846 11.0703L8.03452 8.53396L2.99687 11.1221L1.8463 10.4616L6.49733 8.14967C6.68152 8.0581 6.67994 7.79467 6.49464 7.7052ZM1.59379 6.2161V5.80622L5.94422 7.97676L5.50442 8.15248L1.59379 6.2161ZM10.5145 8.17656C10.5085 8.17293 10.0978 7.97915 10.0978 7.97915L14.4643 5.78162V6.19331L10.5145 8.17656ZM10.9516 8.39178L14.7894 6.46475C14.8085 6.45517 14.8245 6.44047 14.8357 6.42231C14.8469 6.40415 14.8528 6.38324 14.8528 6.3619V5.45028C14.8528 5.42912 14.847 5.40836 14.8359 5.3903C14.8249 5.37224 14.8091 5.35757 14.7903 5.3479L13.0001 4.43096C12.9839 4.42269 12.966 4.41836 12.9478 4.41833C12.9297 4.4183 12.9118 4.42257 12.8956 4.43078L8.086 6.87136C8.06999 6.87947 8.05231 6.88373 8.03436 6.88379C8.01641 6.88385 7.99869 6.87971 7.98263 6.87171L3.08839 4.43008C3.07231 4.42208 3.05458 4.41794 3.03662 4.41802C3.01866 4.41809 3.00097 4.42236 2.98495 4.43049L1.21021 5.33241C1.19127 5.34203 1.17536 5.3567 1.16424 5.3748C1.15313 5.3929 1.14723 5.41373 1.14722 5.43497V6.36149C1.14722 6.40526 1.17205 6.44523 1.21132 6.46463L5.06532 8.36876L1.22915 10.2735C1.2103 10.2829 1.19438 10.2973 1.18313 10.3151C1.17188 10.3329 1.16572 10.3534 1.16533 10.3744L1.14856 11.2855C1.14817 11.3071 1.15389 11.3284 1.16505 11.3469C1.17621 11.3655 1.19237 11.3805 1.21167 11.3903L3.05455 12.3227C3.0706 12.3308 3.08833 12.335 3.10631 12.335C3.1243 12.3351 3.14203 12.3309 3.1581 12.3228L7.98257 9.90028C7.99867 9.8922 8.01644 9.88801 8.03446 9.88805C8.05247 9.88809 8.07023 9.89236 8.0863 9.90051L12.8507 12.323C12.8665 12.331 12.8839 12.3352 12.9016 12.3354C12.9194 12.3356 12.9369 12.3317 12.9528 12.324L14.7879 11.4355C14.8073 11.4261 14.8237 11.4114 14.8352 11.3931C14.8467 11.3748 14.8528 11.3537 14.8528 11.3321V10.3801C14.8528 10.3586 14.8468 10.3375 14.8354 10.3193C14.8241 10.301 14.8078 10.2863 14.7885 10.2769L10.9516 8.39178Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
7
frontend/app/svg/icons/pwright.svg
Normal file
7
frontend/app/svg/icons/pwright.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.4578 9.23896C4.94236 9.38524 4.6042 9.64172 4.38144 9.898C4.5948 9.71128 4.8806 9.53992 5.26612 9.43064C5.66044 9.31888 5.99684 9.31968 6.2748 9.37332V9.15596C6.03768 9.13428 5.76584 9.15156 5.4578 9.23896ZM4.35788 7.41176L2.44362 7.91608C2.44362 7.91608 2.47851 7.96536 2.54311 8.03112L4.16616 7.60344C4.16616 7.60344 4.14316 7.8998 3.94343 8.16492C4.32124 7.87908 4.35788 7.41176 4.35788 7.41176ZM5.96024 11.9106C3.26637 12.6362 1.84113 9.51424 1.40962 7.89384C1.21026 7.14588 1.12323 6.5794 1.10004 6.21384C1.09755 6.17588 1.0987 6.14388 1.10138 6.11456C0.961639 6.123 0.894735 6.19564 0.908347 6.40556C0.931543 6.77092 1.01857 7.33736 1.21793 8.08556C1.64924 9.70572 3.07467 12.8276 5.76856 12.1021C6.35492 11.9441 6.79544 11.6564 7.12612 11.2891C6.82132 11.5644 6.43984 11.7812 5.96024 11.9106ZM6.46648 5.50112V5.69284H7.52312C7.50144 5.62496 7.4796 5.5638 7.45792 5.50112H6.46648Z" fill="#2D4552"/>
|
||||
<path d="M7.75929 7.08008C8.23449 7.21504 8.48581 7.5482 8.61865 7.84304L9.14849 7.99352C9.14849 7.99352 9.07621 6.96164 8.14285 6.69652C7.26969 6.44844 6.73237 7.18168 6.66701 7.27656C6.92101 7.0956 7.29193 6.94744 7.75929 7.08008ZM11.9769 7.8478C11.103 7.5986 10.5658 8.33336 10.5014 8.42692C10.7556 8.24616 11.1264 8.09796 11.5935 8.2312C12.068 8.36636 12.3191 8.69912 12.4523 8.99416L12.9829 9.1452C12.9829 9.1452 12.9095 8.11312 11.9769 7.8478ZM11.4505 10.5685L7.04293 9.33632C7.04293 9.33632 7.09065 9.57824 7.27373 9.89148L10.9847 10.9289C11.2902 10.7522 11.4505 10.5685 11.4505 10.5685ZM8.39473 13.2208C4.90477 12.2851 5.32669 7.83844 5.89141 5.73132C6.12393 4.86296 6.36297 4.21753 6.56121 3.78488C6.44293 3.76053 6.34497 3.82284 6.24817 4.01971C6.03769 4.4466 5.76853 5.14168 5.50805 6.11472C4.94349 8.2218 4.52157 12.6683 8.01137 13.604C9.65629 14.0447 10.9377 13.3749 11.893 12.3231C10.9862 13.1443 9.82861 13.6048 8.39473 13.2208Z" fill="#2D4552"/>
|
||||
<path d="M6.46647 10.8686V9.97124L3.97332 10.6782C3.97332 10.6782 4.15755 9.6078 5.45779 9.23896C5.85211 9.1272 6.18855 9.12796 6.46647 9.18164V5.50112H7.71479C7.57887 5.08112 7.44739 4.75776 7.33695 4.53308C7.15427 4.1612 6.96699 4.40772 6.54183 4.76332C6.24239 5.01348 5.48559 5.54716 4.34675 5.85404C3.20789 6.16112 2.28719 6.07968 1.90304 6.01316C1.35843 5.9192 1.07358 5.7996 1.10022 6.21384C1.12342 6.5792 1.21045 7.14568 1.40981 7.89384C1.84112 9.51404 3.26655 12.636 5.96043 11.9104C6.66411 11.7208 7.16079 11.346 7.50507 10.8684H6.46647V10.8686ZM2.44342 7.91608L4.35787 7.41176C4.35787 7.41176 4.30207 8.14824 3.58438 8.33744C2.86648 8.52644 2.44342 7.91608 2.44342 7.91608Z" fill="#E2574C"/>
|
||||
<path d="M13.6714 5.54369C13.1738 5.63093 11.9799 5.73961 10.5044 5.34413C9.0286 4.94889 8.0494 4.25762 7.66144 3.9327C7.11148 3.47206 6.86956 3.15193 6.63148 3.63615C6.421 4.06325 6.15184 4.75833 5.89132 5.73137C5.3268 7.83845 4.90488 12.285 8.39464 13.2206C11.8837 14.1555 13.7412 10.0935 14.3057 7.98625C14.5662 7.01341 14.6805 6.27673 14.7119 5.80173C14.7478 5.26365 14.3782 5.41985 13.6714 5.54369ZM6.65984 7.28697C6.65984 7.28697 7.2098 6.43161 8.14256 6.69673C9.07592 6.96185 9.1482 7.99373 9.1482 7.99373L6.65984 7.28697ZM8.93676 11.1252C7.29608 10.6446 7.04304 9.33633 7.04304 9.33633L11.4504 10.5686C11.4504 10.5684 10.5608 11.5998 8.93676 11.1252ZM10.495 8.43653C10.495 8.43653 11.0442 7.58177 11.9768 7.84765C12.9094 8.11317 12.9828 9.14505 12.9828 9.14505L10.495 8.43653Z" fill="#2EAD33"/>
|
||||
<path d="M5.67788 10.1948L5.24292 10.3182C5.34568 10.8975 5.5268 11.4534 5.81108 11.9445C5.86056 11.9336 5.90964 11.9242 5.96004 11.9104C6.09212 11.8748 6.2146 11.8306 6.33252 11.7825C6.01488 11.3112 5.80476 10.7683 5.67788 10.1948ZM5.50804 6.11477C5.28452 6.94901 5.08456 8.14977 5.1396 9.35417C5.23812 9.31141 5.3422 9.27157 5.4578 9.23877L5.53832 9.22077C5.44016 7.93429 5.65236 6.62333 5.8914 5.73137C5.952 5.50573 6.01276 5.29585 6.07332 5.10013C5.97576 5.16221 5.87072 5.22585 5.75108 5.29141C5.6704 5.54045 5.58892 5.81265 5.50804 6.11477Z" fill="#C04B41"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
|
|
@ -145,5 +145,5 @@
|
|||
"engines": {
|
||||
"node": ">=20.18.0"
|
||||
},
|
||||
"packageManager": "yarn@4.6.0"
|
||||
"packageManager": "yarn@4.7.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue