-
Notifications
You must be signed in to change notification settings - Fork 88
/
stories-browser.ts
145 lines (136 loc) · 4.67 KB
/
stories-browser.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { BaseBrowser, BaseBrowserOptions } from './base-browser';
import { Logger } from '../logger';
import { NoStoriesError, StoriesTimeoutError } from '../errors';
import { Story } from '../story-types';
import { StorybookConnection } from '../storybook-connection';
interface API {
store?: () => {
_configuring?: boolean; // available SB v6 or later
};
storyStore?: {
// available SB v6.4 or later
cacheAllCSFFiles: () => Promise<void>;
cachedCSFFiles?: Record<string, unknown>;
};
raw?: () => { id: string; kind: string; name: string }[]; // available SB v5 or later
}
interface PreviewAPI extends API {
storyStoreValue?: {
cacheAllCSFFiles: () => Promise<void>;
cachedCSFFiles?: Record<string, unknown>;
extract: () => Record<string, { id: string; kind: string; name: string }>; // available in SB v8
};
}
type ExposedWindow = typeof window & {
__STORYBOOK_CLIENT_API__?: API;
// available SB v8 or later
__STORYBOOK_PREVIEW__?: PreviewAPI;
};
/**
*
* Browser class to fetch all stories names.
*
**/
export class StoriesBrowser extends BaseBrowser {
/**
*
* @param connection Connected connection to the target Storybook server
* @param opt Options to launch browser
* @param logger Logger instance
*
**/
constructor(
protected connection: StorybookConnection,
protected opt: BaseBrowserOptions = {},
protected logger: Logger = new Logger('silent'),
) {
super(opt);
}
/**
*
* Fetches stories' id, kind and names
*
* @returns List of stories
*
* @remarks
* This method automatically detects version of the Storybook.
*
**/
async getStories() {
this.logger.debug('Wait for stories definition.');
await this.page.goto(this.connection.url);
let stories: Story[] | null = null;
await this.page.goto(
this.connection.url + '/iframe.html?selectedKind=story-crawler-kind&selectedStory=story-crawler-story',
{
timeout: 60_000,
waitUntil: 'domcontentloaded',
},
);
await this.page.waitForFunction(
() =>
(window as ExposedWindow).__STORYBOOK_CLIENT_API__ ||
(window as ExposedWindow).__STORYBOOK_PREVIEW__?.storyStoreValue,
{
timeout: 60_000,
},
);
await this.page.waitForTimeout(500);
await this.page.evaluate(() => {
const api = (window as ExposedWindow).__STORYBOOK_CLIENT_API__ || (window as ExposedWindow).__STORYBOOK_PREVIEW__;
function isPreviewApi(api: API | PreviewAPI): api is PreviewAPI {
return (api as PreviewAPI).storyStoreValue !== undefined;
}
if (api === undefined) return;
if (isPreviewApi(api)) {
return api.storyStoreValue && api.storyStoreValue.cacheAllCSFFiles();
}
return api.storyStore?.cacheAllCSFFiles && api.storyStore.cacheAllCSFFiles();
});
const result = await this.page.evaluate(() => {
function isPreviewApi(api: API | PreviewAPI): api is PreviewAPI {
return (api as PreviewAPI).storyStoreValue !== undefined;
}
return new Promise<{ stories: Story[] | null; timeout: boolean }>(res => {
const getStories = (count = 0) => {
const MAX_CONFIGURE_WAIT_COUNT = 4_000;
const api =
(window as ExposedWindow).__STORYBOOK_CLIENT_API__ || (window as ExposedWindow).__STORYBOOK_PREVIEW__;
if (api === undefined) return;
// for Storybook v6
const configuring = !isPreviewApi(api) && api.store && api.store()._configuring;
// for Storybook v6 and 'storyStoreV7' option
const configuringV7store = !isPreviewApi(api) && api.storyStore && !api.storyStore.cachedCSFFiles;
// for Storybook v8
const configuringV8store = isPreviewApi(api) && api.storyStoreValue && !api.storyStoreValue.cachedCSFFiles;
if (configuring || configuringV7store || configuringV8store) {
if (count < MAX_CONFIGURE_WAIT_COUNT) {
setTimeout(() => getStories(++count), 16);
} else {
res({ stories: null, timeout: true });
}
return;
}
const stories = (
isPreviewApi(api) && api.storyStoreValue
? Object.values(api.storyStoreValue.extract())
: api.raw
? api.raw()
: []
).map(_ => ({ id: _.id, kind: _.kind, story: _.name, version: 'v5' }) as Story);
res({ stories, timeout: false });
};
getStories();
});
});
if (result.timeout) {
throw new StoriesTimeoutError();
}
stories = result.stories;
if (!stories) {
throw new NoStoriesError();
}
this.logger.debug(stories);
return stories;
}
}