creevey 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/addon/components/Panel.d.ts +1 -1
- package/dist/client/addon/components/Panel.js +5 -2
- package/dist/client/addon/components/Panel.js.map +1 -1
- package/dist/client/addon/withCreevey.js +1 -1
- package/dist/client/addon/withCreevey.js.map +1 -1
- package/dist/client/shared/components/PageFooter/PageFooter.d.ts +1 -3
- package/dist/client/shared/components/PageFooter/PageFooter.js +3 -8
- package/dist/client/shared/components/PageFooter/PageFooter.js.map +1 -1
- package/dist/client/shared/components/PageFooter/Paging.d.ts +1 -1
- package/dist/client/shared/components/PageFooter/Paging.js +4 -21
- package/dist/client/shared/components/PageFooter/Paging.js.map +1 -1
- package/dist/client/shared/components/PageHeader/PageHeader.d.ts +2 -2
- package/dist/client/shared/components/PageHeader/PageHeader.js +17 -10
- package/dist/client/shared/components/PageHeader/PageHeader.js.map +1 -1
- package/dist/client/shared/components/ResultsPage.d.ts +8 -6
- package/dist/client/shared/components/ResultsPage.js +5 -13
- package/dist/client/shared/components/ResultsPage.js.map +1 -1
- package/dist/client/shared/creeveyClientApi.d.ts +1 -0
- package/dist/client/shared/creeveyClientApi.js +3 -0
- package/dist/client/shared/creeveyClientApi.js.map +1 -1
- package/dist/client/shared/helpers.d.ts +2 -1
- package/dist/client/shared/helpers.js +23 -8
- package/dist/client/shared/helpers.js.map +1 -1
- package/dist/client/web/CreeveyApp.js +45 -8
- package/dist/client/web/CreeveyApp.js.map +1 -1
- package/dist/client/web/CreeveyContext.d.ts +3 -0
- package/dist/client/web/CreeveyContext.js +28 -4
- package/dist/client/web/CreeveyContext.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/Checkbox.d.ts +1 -1
- package/dist/client/web/CreeveyView/SideBar/Checkbox.js +5 -5
- package/dist/client/web/CreeveyView/SideBar/Checkbox.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBar.d.ts +2 -2
- package/dist/client/web/CreeveyView/SideBar/SideBar.js +23 -13
- package/dist/client/web/CreeveyView/SideBar/SideBar.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.d.ts +1 -0
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js +32 -0
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js.map +1 -0
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js +1 -1
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.d.ts +8 -3
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js +38 -23
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestLink.js +6 -5
- package/dist/client/web/CreeveyView/SideBar/TestLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.js +13 -6
- package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.d.ts +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.js +6 -4
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.js.map +1 -1
- package/dist/client/web/main.js +8 -8
- package/dist/server/master/api.js +4 -0
- package/dist/server/master/api.js.map +1 -1
- package/dist/server/master/runner.d.ts +2 -0
- package/dist/server/master/runner.js +59 -9
- package/dist/server/master/runner.js.map +1 -1
- package/dist/server/selenium/browser.js +9 -6
- package/dist/server/selenium/browser.js.map +1 -1
- package/dist/server/worker/worker.js +1 -0
- package/dist/server/worker/worker.js.map +1 -1
- package/dist/types.d.ts +4 -2
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
- package/src/client/addon/components/Panel.tsx +7 -3
- package/src/client/addon/withCreevey.ts +1 -1
- package/src/client/shared/components/PageFooter/PageFooter.tsx +2 -20
- package/src/client/shared/components/PageFooter/Paging.tsx +22 -37
- package/src/client/shared/components/PageHeader/PageHeader.tsx +20 -14
- package/src/client/shared/components/ResultsPage.tsx +18 -31
- package/src/client/shared/creeveyClientApi.ts +4 -0
- package/src/client/shared/helpers.ts +22 -8
- package/src/client/web/CreeveyApp.tsx +66 -13
- package/src/client/web/CreeveyContext.tsx +7 -1
- package/src/client/web/CreeveyView/SideBar/Checkbox.tsx +5 -5
- package/src/client/web/CreeveyView/SideBar/SideBar.tsx +29 -18
- package/src/client/web/CreeveyView/SideBar/SideBarFooter.tsx +38 -0
- package/src/client/web/CreeveyView/SideBar/SideBarHeader.tsx +1 -1
- package/src/client/web/CreeveyView/SideBar/SuiteLink.tsx +50 -31
- package/src/client/web/CreeveyView/SideBar/TestLink.tsx +8 -6
- package/src/client/web/CreeveyView/SideBar/TestStatusIcon.tsx +12 -6
- package/src/client/web/CreeveyView/SideBar/TestsStatus.tsx +7 -3
- package/src/server/master/api.ts +4 -0
- package/src/server/master/runner.ts +65 -9
- package/src/server/selenium/browser.ts +13 -10
- package/src/server/worker/worker.ts +1 -0
- package/src/types.ts +4 -3
@@ -0,0 +1,38 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { styled, withTheme } from '@storybook/theming';
|
3
|
+
import { Button, Icons } from '@storybook/components';
|
4
|
+
import { useCreeveyContext } from '../../CreeveyContext.js';
|
5
|
+
|
6
|
+
const Sticky = withTheme(
|
7
|
+
styled.div(({ theme }) => ({
|
8
|
+
padding: '24px 16px 8px 16px',
|
9
|
+
background: theme.background.content,
|
10
|
+
height: '50px',
|
11
|
+
zIndex: 5,
|
12
|
+
position: 'sticky',
|
13
|
+
bottom: '0',
|
14
|
+
})),
|
15
|
+
);
|
16
|
+
|
17
|
+
const Container = styled.div({
|
18
|
+
display: 'flex',
|
19
|
+
justifyContent: 'space-between',
|
20
|
+
});
|
21
|
+
|
22
|
+
export function SideBarFooter(): JSX.Element {
|
23
|
+
const { onApproveAll, onImageApprove } = useCreeveyContext();
|
24
|
+
|
25
|
+
return (
|
26
|
+
<Sticky>
|
27
|
+
<Container>
|
28
|
+
<Button secondary onClick={onImageApprove} disabled={!onImageApprove} style={{ paddingRight: 8 }}>
|
29
|
+
Approve
|
30
|
+
<Icons icon="arrowright" style={{ paddingLeft: 4 }} />
|
31
|
+
</Button>
|
32
|
+
<Button secondary outline onClick={onApproveAll}>
|
33
|
+
Approve all
|
34
|
+
</Button>
|
35
|
+
</Container>
|
36
|
+
</Sticky>
|
37
|
+
);
|
38
|
+
}
|
@@ -14,49 +14,67 @@ export interface SuiteLinkProps {
|
|
14
14
|
}
|
15
15
|
|
16
16
|
export const Container = withTheme(
|
17
|
-
styled.div<{ theme: Theme; disabled?: boolean
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
styled.div<{ theme: Theme; disabled?: boolean; active?: boolean; focused?: boolean }>(
|
18
|
+
({ theme, disabled, active, focused }) => ({
|
19
|
+
position: 'relative',
|
20
|
+
width: '100%',
|
21
|
+
height: '28px',
|
22
|
+
lineHeight: '20px',
|
23
|
+
display: 'flex',
|
24
|
+
background: active ? theme.color.secondary : focused ? theme.background.hoverable : 'none',
|
25
|
+
color: active ? theme.color.inverseText : 'inherit',
|
26
|
+
outline: focused ? `1px solid ${theme.color.ancillary}` : 'none',
|
27
|
+
...(disabled ? { color: theme.color.mediumdark, pointerEvents: 'none' } : {}),
|
28
|
+
|
29
|
+
// NOTE There is no way to trigger hover from js, so we add `.hover` class for testing purpose
|
30
|
+
'&:hover, &.hover': active
|
31
|
+
? {}
|
32
|
+
: {
|
33
|
+
background: theme.background.hoverable,
|
34
|
+
},
|
35
|
+
}),
|
36
|
+
),
|
22
37
|
);
|
23
38
|
|
24
39
|
export const Button = withTheme(
|
25
|
-
styled.button<{ theme: Theme; active?: boolean
|
26
|
-
|
40
|
+
styled.button<{ theme: Theme; active?: boolean }>(({ theme, active }) => ({
|
41
|
+
flexGrow: 1,
|
27
42
|
boxSizing: 'border-box',
|
28
43
|
appearance: 'none',
|
29
|
-
padding: '
|
30
|
-
lineHeight: '
|
44
|
+
padding: '4px 16px 4px 8px',
|
45
|
+
lineHeight: '18px',
|
31
46
|
cursor: 'pointer',
|
32
47
|
border: 'none',
|
33
48
|
zIndex: 1,
|
34
49
|
textAlign: 'left',
|
35
|
-
background:
|
50
|
+
background: 'none',
|
51
|
+
outline: 'none',
|
36
52
|
color: active ? theme.color.inverseText : 'inherit',
|
37
|
-
outline: focused ? `1px solid ${theme.color.ancillary}` : 'none',
|
38
|
-
|
39
|
-
// NOTE There is no way to trigger hover from js, so we add `.hover` class for testing purpose
|
40
|
-
'&:hover, &.hover': active
|
41
|
-
? {}
|
42
|
-
: {
|
43
|
-
background: theme.background.hoverable,
|
44
|
-
},
|
45
53
|
})),
|
46
54
|
);
|
47
55
|
|
48
56
|
const ArrowIcon = styled(Icons)({
|
49
|
-
paddingRight: '
|
57
|
+
paddingRight: '4px',
|
50
58
|
display: 'inline-block',
|
51
|
-
width: '
|
52
|
-
height: '
|
59
|
+
width: '12px',
|
60
|
+
height: '18px',
|
61
|
+
verticalAlign: 'unset',
|
53
62
|
});
|
54
63
|
|
55
64
|
export const SuiteContainer = styled.span<{ padding: number }>(({ padding }) => ({
|
56
65
|
paddingLeft: padding,
|
57
66
|
whiteSpace: 'normal',
|
67
|
+
display: 'grid',
|
68
|
+
gridTemplateColumns: 'repeat(2, min-content) auto',
|
58
69
|
}));
|
59
70
|
|
71
|
+
export const SuiteTitle = styled.span({
|
72
|
+
paddingLeft: '4px',
|
73
|
+
whiteSpace: 'nowrap',
|
74
|
+
overflowX: 'hidden',
|
75
|
+
textOverflow: 'ellipsis',
|
76
|
+
});
|
77
|
+
|
60
78
|
export function SuiteLink({ title, suite, 'data-testid': dataTid }: SuiteLinkProps): JSX.Element {
|
61
79
|
const { onSuiteOpen, onSuiteToggle } = useContext(CreeveyContext);
|
62
80
|
const { sidebarFocusedItem, setSidebarFocusedItem } = useContext(KeyboardEventsContext);
|
@@ -70,6 +88,7 @@ export function SuiteLink({ title, suite, 'data-testid': dataTid }: SuiteLinkPro
|
|
70
88
|
sidebarFocusedItem.every((x) => suite.path.includes(x)),
|
71
89
|
[suite, sidebarFocusedItem],
|
72
90
|
);
|
91
|
+
|
73
92
|
useEffect(
|
74
93
|
() => (suite.indeterminate ? checkboxRef.current?.setIndeterminate() : checkboxRef.current?.resetIndeterminate()),
|
75
94
|
[suite.indeterminate],
|
@@ -95,16 +114,7 @@ export function SuiteLink({ title, suite, 'data-testid': dataTid }: SuiteLinkPro
|
|
95
114
|
};
|
96
115
|
|
97
116
|
return (
|
98
|
-
<Container>
|
99
|
-
<Button onClick={handleOpen} onFocus={handleFocus} data-testid={dataTid} focused={isSuiteFocused} ref={buttonRef}>
|
100
|
-
<TestStatusIcon status={suite.status} skip={suite.skip} />
|
101
|
-
<SuiteContainer padding={Math.max(48, (suite.path.length + 5) * 8)}>
|
102
|
-
{isTest(suite) ||
|
103
|
-
(Boolean(suite.path.length) &&
|
104
|
-
(suite.opened ? <ArrowIcon icon="arrowdown" /> : <ArrowIcon icon="arrowright" />))}
|
105
|
-
{title}
|
106
|
-
</SuiteContainer>
|
107
|
-
</Button>
|
117
|
+
<Container focused={isSuiteFocused}>
|
108
118
|
<CheckboxContainer>
|
109
119
|
<Checkbox
|
110
120
|
ref={checkboxRef}
|
@@ -113,6 +123,15 @@ export function SuiteLink({ title, suite, 'data-testid': dataTid }: SuiteLinkPro
|
|
113
123
|
onValueChange={handleCheck}
|
114
124
|
/>
|
115
125
|
</CheckboxContainer>
|
126
|
+
<Button onClick={handleOpen} onFocus={handleFocus} data-testid={dataTid} ref={buttonRef}>
|
127
|
+
<SuiteContainer padding={(suite.path.length - 1) * 8}>
|
128
|
+
{isTest(suite) ||
|
129
|
+
(Boolean(suite.path.length) &&
|
130
|
+
(suite.opened ? <ArrowIcon icon="arrowdown" /> : <ArrowIcon icon="arrowright" />))}
|
131
|
+
<TestStatusIcon status={suite.status} skip={suite.skip} />
|
132
|
+
<SuiteTitle>{title}</SuiteTitle>
|
133
|
+
</SuiteContainer>
|
134
|
+
</Button>
|
116
135
|
</Container>
|
117
136
|
);
|
118
137
|
}
|
@@ -4,7 +4,7 @@ import { TestStatusIcon } from './TestStatusIcon.js';
|
|
4
4
|
import { CreeveyContext } from '../../CreeveyContext.js';
|
5
5
|
import { SideBarContext } from './SideBar.js';
|
6
6
|
import { KeyboardEventsContext } from '../../KeyboardEventsContext.js';
|
7
|
-
import { Button, Container, SuiteContainer } from './SuiteLink.js';
|
7
|
+
import { Button, Container, SuiteContainer, SuiteTitle } from './SuiteLink.js';
|
8
8
|
import { Checkbox, CheckboxContainer } from './Checkbox.js';
|
9
9
|
import { getTestPath } from '../../../shared/helpers.js';
|
10
10
|
|
@@ -48,11 +48,7 @@ export function TestLink({ title, opened, test }: TestLinkProps): JSX.Element {
|
|
48
48
|
}, [test, onOpenTest, setSidebarFocusedItem]);
|
49
49
|
|
50
50
|
return (
|
51
|
-
<Container disabled={emptyResults}>
|
52
|
-
<Button onClick={handleOpen} active={opened} focused={isTestFocused} disabled={emptyResults} ref={buttonRef}>
|
53
|
-
<TestStatusIcon inverted={opened} status={test.status} skip={test.skip} />
|
54
|
-
<SuiteContainer padding={(testPath.length + 8) * 8}>{title}</SuiteContainer>
|
55
|
-
</Button>
|
51
|
+
<Container disabled={emptyResults} active={opened} focused={isTestFocused}>
|
56
52
|
{/* NOTE Little hack to allow click on checkbox and don't trigger Button click */}
|
57
53
|
{/* We can use other approach, but checkbox has vertical-align: top */}
|
58
54
|
<CheckboxContainer>
|
@@ -62,6 +58,12 @@ export function TestLink({ title, opened, test }: TestLinkProps): JSX.Element {
|
|
62
58
|
onValueChange={handleCheck}
|
63
59
|
/>
|
64
60
|
</CheckboxContainer>
|
61
|
+
<Button onClick={handleOpen} disabled={emptyResults} ref={buttonRef}>
|
62
|
+
<SuiteContainer padding={(testPath.length + 1) * 8}>
|
63
|
+
<TestStatusIcon inverted={opened} status={test.status} skip={test.skip} />
|
64
|
+
<SuiteTitle>{title}</SuiteTitle>
|
65
|
+
</SuiteContainer>
|
66
|
+
</Button>
|
65
67
|
</Container>
|
66
68
|
);
|
67
69
|
}
|
@@ -11,21 +11,22 @@ export interface TestStatusIconProps {
|
|
11
11
|
}
|
12
12
|
|
13
13
|
const Container = styled.span({
|
14
|
-
width:
|
15
|
-
height:
|
14
|
+
width: '16px',
|
15
|
+
height: '13px',
|
16
16
|
padding: 1,
|
17
17
|
display: 'inline-block',
|
18
18
|
});
|
19
19
|
|
20
20
|
const Icon = styled(Icons)({
|
21
21
|
position: 'relative',
|
22
|
-
top: '
|
22
|
+
top: '1px',
|
23
23
|
verticalAlign: 'baseline',
|
24
24
|
});
|
25
25
|
|
26
26
|
const Spinner = styled(Loader)({
|
27
|
-
top: '
|
28
|
-
left: '
|
27
|
+
top: '12px',
|
28
|
+
left: 'unset',
|
29
|
+
marginLeft: '0px',
|
29
30
|
});
|
30
31
|
|
31
32
|
export const TestStatusIcon = withTheme(
|
@@ -40,6 +41,10 @@ export const TestStatusIcon = withTheme(
|
|
40
41
|
icon = <Icon color={inverted ? theme.color.lightest : theme.color.green} icon="check" />;
|
41
42
|
break;
|
42
43
|
}
|
44
|
+
case 'approved': {
|
45
|
+
icon = <Icon color={inverted ? theme.color.lightest : theme.color.mediumdark} icon="thumbsup" />;
|
46
|
+
break;
|
47
|
+
}
|
43
48
|
case 'running': {
|
44
49
|
icon = <Spinner size={10} />;
|
45
50
|
break;
|
@@ -49,7 +54,8 @@ export const TestStatusIcon = withTheme(
|
|
49
54
|
break;
|
50
55
|
}
|
51
56
|
default: {
|
52
|
-
if (skip) icon = <Icon color={inverted ? theme.color.lightest : undefined} icon="
|
57
|
+
if (skip) icon = <Icon color={inverted ? theme.color.lightest : undefined} icon="alert" />;
|
58
|
+
else icon = <Icon color={inverted ? theme.color.lightest : undefined} icon="circlehollow" />;
|
53
59
|
break;
|
54
60
|
}
|
55
61
|
}
|
@@ -53,7 +53,7 @@ export const TestsStatus = withTheme(
|
|
53
53
|
successCount,
|
54
54
|
failedCount,
|
55
55
|
pendingCount,
|
56
|
-
|
56
|
+
approvedCount,
|
57
57
|
onClickByStatus,
|
58
58
|
theme,
|
59
59
|
}: TestsStatusProps): JSX.Element => {
|
@@ -94,9 +94,13 @@ export const TestsStatus = withTheme(
|
|
94
94
|
</Button>
|
95
95
|
<Divider />
|
96
96
|
{/* @ts-expect-error Fixed in https://github.com/storybookjs/storybook/pull/26623 */}
|
97
|
-
<Button
|
97
|
+
<Button
|
98
|
+
onClick={() => {
|
99
|
+
onClickByStatus('approved');
|
100
|
+
}}
|
101
|
+
>
|
98
102
|
<IconContainer color={theme?.color.defaultText}>
|
99
|
-
<Icons icon="
|
103
|
+
<Icons icon="thumbsup" /> {approvedCount}
|
100
104
|
</IconContainer>
|
101
105
|
</Button>
|
102
106
|
</Container>
|
package/src/server/master/api.ts
CHANGED
@@ -45,6 +45,7 @@ export default class Runner extends EventEmitter {
|
|
45
45
|
// TODO Handle 'retrying' status
|
46
46
|
test.status = status == 'retrying' ? 'failed' : status;
|
47
47
|
if (!result) {
|
48
|
+
// NOTE: Running status
|
48
49
|
this.sendUpdate({ tests: { [id]: { id, browser, testName, storyPath, status: test.status, storyId } } });
|
49
50
|
return;
|
50
51
|
}
|
@@ -53,8 +54,23 @@ export default class Runner extends EventEmitter {
|
|
53
54
|
}
|
54
55
|
test.results.push(result);
|
55
56
|
|
57
|
+
if (status == 'failed') {
|
58
|
+
test.approved = null;
|
59
|
+
}
|
60
|
+
|
56
61
|
this.sendUpdate({
|
57
|
-
tests: {
|
62
|
+
tests: {
|
63
|
+
[id]: {
|
64
|
+
id,
|
65
|
+
browser,
|
66
|
+
testName,
|
67
|
+
storyPath,
|
68
|
+
status: test.status,
|
69
|
+
approved: test.approved,
|
70
|
+
results: [result],
|
71
|
+
storyId,
|
72
|
+
},
|
73
|
+
},
|
58
74
|
});
|
59
75
|
|
60
76
|
if (this.failFast && status == 'failed') this.stop();
|
@@ -161,6 +177,44 @@ export default class Runner extends EventEmitter {
|
|
161
177
|
};
|
162
178
|
}
|
163
179
|
|
180
|
+
private async copyImage(test: ServerTest, image: string, actual: string): Promise<void> {
|
181
|
+
const { browser, testName, storyPath } = test;
|
182
|
+
const restPath = [...storyPath, testName].filter(isDefined);
|
183
|
+
const testPath = path.join(...restPath, image == browser ? '' : browser);
|
184
|
+
const srcImagePath = path.join(this.reportDir, testPath, actual);
|
185
|
+
const dstImagePath = path.join(this.screenDir, testPath, `${image}.png`);
|
186
|
+
await mkdir(path.join(this.screenDir, testPath), { recursive: true });
|
187
|
+
await copyFile(srcImagePath, dstImagePath);
|
188
|
+
}
|
189
|
+
|
190
|
+
public async approveAll(): Promise<void> {
|
191
|
+
const updatedTests: NonNullable<CreeveyUpdate['tests']> = {};
|
192
|
+
for (const test of Object.values(this.tests)) {
|
193
|
+
if (!test?.results) continue;
|
194
|
+
const retry = test.results.length - 1;
|
195
|
+
const { images, status } = test.results.at(retry) ?? {};
|
196
|
+
if (!images || status != 'failed') continue;
|
197
|
+
for (const [name, image] of Object.entries(images)) {
|
198
|
+
if (!image) continue;
|
199
|
+
await this.copyImage(test, name, image.actual);
|
200
|
+
|
201
|
+
if (!test.approved) {
|
202
|
+
test.approved = {};
|
203
|
+
}
|
204
|
+
test.approved[name] = retry;
|
205
|
+
|
206
|
+
updatedTests[test.id] = {
|
207
|
+
id: test.id,
|
208
|
+
browser: test.browser,
|
209
|
+
storyPath: test.storyPath,
|
210
|
+
storyId: test.storyId,
|
211
|
+
approved: { [name]: retry },
|
212
|
+
};
|
213
|
+
}
|
214
|
+
}
|
215
|
+
this.sendUpdate({ tests: updatedTests });
|
216
|
+
}
|
217
|
+
|
164
218
|
public async approve({ id, retry, image }: ApprovePayload): Promise<void> {
|
165
219
|
const test = this.tests[id];
|
166
220
|
if (!test?.results) return;
|
@@ -171,16 +225,18 @@ export default class Runner extends EventEmitter {
|
|
171
225
|
if (!test.approved) {
|
172
226
|
test.approved = {};
|
173
227
|
}
|
174
|
-
const { browser, testName, storyPath } = test;
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
const dstImagePath = path.join(this.screenDir, testPath, `${image}.png`);
|
179
|
-
await mkdir(path.join(this.screenDir, testPath), { recursive: true });
|
180
|
-
await copyFile(srcImagePath, dstImagePath);
|
228
|
+
const { browser, testName, storyPath, storyId } = test;
|
229
|
+
|
230
|
+
await this.copyImage(test, image, images.actual);
|
231
|
+
|
181
232
|
test.approved[image] = retry;
|
233
|
+
|
234
|
+
if (Object.keys(result.images).every((name) => typeof test.approved?.[name] == 'number')) {
|
235
|
+
test.status = 'approved';
|
236
|
+
}
|
237
|
+
|
182
238
|
this.sendUpdate({
|
183
|
-
tests: { [id]: { id, browser, testName, storyPath, approved: { [image]: retry }, storyId
|
239
|
+
tests: { [id]: { id, browser, testName, storyPath, status: test.status, approved: { [image]: retry }, storyId } },
|
184
240
|
});
|
185
241
|
}
|
186
242
|
private sendUpdate(data: CreeveyUpdate): void {
|
@@ -55,6 +55,7 @@ let browserName = '';
|
|
55
55
|
let browser: WebDriver | null = null;
|
56
56
|
// let context: UnPromise<ReturnType<typeof BrowsingContext>> | null = null;
|
57
57
|
let creeveyServerHost: string | null = null;
|
58
|
+
let creeveyServerPort: number | null = null;
|
58
59
|
|
59
60
|
function getSessionData(grid: string, sessionId = ''): Promise<Record<string, unknown>> {
|
60
61
|
const gridUrl = new URL(grid);
|
@@ -520,11 +521,10 @@ async function openStorybookPage(
|
|
520
521
|
}
|
521
522
|
}
|
522
523
|
|
523
|
-
async function resolveCreeveyHost(browser: WebDriver, port: number): Promise<
|
524
|
-
if (creeveyServerHost != null) return creeveyServerHost;
|
525
|
-
|
524
|
+
async function resolveCreeveyHost(browser: WebDriver, port: number): Promise<void> {
|
526
525
|
const addresses = getAddresses();
|
527
526
|
|
527
|
+
creeveyServerPort = port;
|
528
528
|
creeveyServerHost = await browser.executeAsyncScript(
|
529
529
|
function (hosts: string[], port: number, callback: (host?: string | null) => void) {
|
530
530
|
void Promise.all(
|
@@ -558,8 +558,6 @@ async function resolveCreeveyHost(browser: WebDriver, port: number): Promise<str
|
|
558
558
|
);
|
559
559
|
|
560
560
|
if (creeveyServerHost == null) throw new Error("Can't reach creevey server from a browser");
|
561
|
-
|
562
|
-
return creeveyServerHost;
|
563
561
|
}
|
564
562
|
|
565
563
|
export async function loadStoriesFromBrowser(): Promise<StoriesRaw> {
|
@@ -690,8 +688,14 @@ export async function getBrowser(config: Config, options: Options & { browser: s
|
|
690
688
|
await updateStorybookGlobals(browser, _storybookGlobals);
|
691
689
|
}
|
692
690
|
|
693
|
-
|
691
|
+
await resolveCreeveyHost(browser, options.port);
|
692
|
+
|
693
|
+
await updateBrowserGlobalVariables(browser);
|
694
|
+
|
695
|
+
return browser;
|
696
|
+
}
|
694
697
|
|
698
|
+
async function updateBrowserGlobalVariables(browser: WebDriver) {
|
695
699
|
await browser.executeScript(
|
696
700
|
function (workerId: number, creeveyHost: string, creeveyPort: number) {
|
697
701
|
window.__CREEVEY_WORKER_ID__ = workerId;
|
@@ -699,11 +703,9 @@ export async function getBrowser(config: Config, options: Options & { browser: s
|
|
699
703
|
window.__CREEVEY_SERVER_PORT__ = creeveyPort;
|
700
704
|
},
|
701
705
|
process.pid,
|
702
|
-
|
703
|
-
|
706
|
+
creeveyServerHost,
|
707
|
+
creeveyServerPort,
|
704
708
|
);
|
705
|
-
|
706
|
-
return browser;
|
707
709
|
}
|
708
710
|
|
709
711
|
async function updateStoryArgs(browser: WebDriver, story: StoryInput, updatedArgs: Args): Promise<void> {
|
@@ -799,6 +801,7 @@ export async function switchStory(this: Context): Promise<void> {
|
|
799
801
|
});
|
800
802
|
});
|
801
803
|
|
804
|
+
await updateBrowserGlobalVariables(this.browser);
|
802
805
|
await resetMousePosition(this.browser);
|
803
806
|
const isCaptureCalled = await selectStory(this.browser, id, waitForReady);
|
804
807
|
|
@@ -208,6 +208,7 @@ export async function start(config: Config, options: Options & { browser: string
|
|
208
208
|
retries = test.retries;
|
209
209
|
|
210
210
|
mocha.grep(new RegExp(`^${testPath}$`));
|
211
|
+
mocha.unloadFiles();
|
211
212
|
const runner = mocha.run(runHandler);
|
212
213
|
|
213
214
|
// TODO How handle browser corruption?
|
package/src/types.ts
CHANGED
@@ -323,7 +323,7 @@ export interface Images {
|
|
323
323
|
error?: string;
|
324
324
|
}
|
325
325
|
|
326
|
-
export type TestStatus = 'unknown' | 'pending' | 'running' | 'failed' | 'success' | 'retrying';
|
326
|
+
export type TestStatus = 'unknown' | 'pending' | 'running' | 'failed' | 'approved' | 'success' | 'retrying';
|
327
327
|
|
328
328
|
export interface TestResult {
|
329
329
|
status: 'failed' | 'success';
|
@@ -350,7 +350,7 @@ export interface TestData extends TestMeta {
|
|
350
350
|
retries?: number;
|
351
351
|
status?: TestStatus;
|
352
352
|
results?: TestResult[];
|
353
|
-
approved?: Partial<Record<string, number
|
353
|
+
approved?: Partial<Record<string, number>> | null;
|
354
354
|
}
|
355
355
|
|
356
356
|
export interface ServerTest extends TestData {
|
@@ -415,7 +415,8 @@ export type Request =
|
|
415
415
|
| { type: 'status' }
|
416
416
|
| { type: 'start'; payload: string[] }
|
417
417
|
| { type: 'stop' }
|
418
|
-
| { type: 'approve'; payload: ApprovePayload }
|
418
|
+
| { type: 'approve'; payload: ApprovePayload }
|
419
|
+
| { type: 'approveAll' };
|
419
420
|
|
420
421
|
export type Response =
|
421
422
|
| { type: 'status'; payload: CreeveyStatus }
|