UNPKG

creevey

Version:

Cross-browser screenshot testing tool for Storybook with fancy UI Runner

208 lines 9.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CreeveyController = exports.ADDON_ID = void 0; const types_1 = require("@storybook/types"); const core_events_1 = require("@storybook/core-events"); const index_js_1 = require("../../shared/index.js"); const types_js_1 = require("../../types.js"); const creeveyClientApi_js_1 = require("../shared/creeveyClientApi.js"); const helpers_js_1 = require("../shared/helpers.js"); const utils_js_1 = require("./utils.js"); exports.ADDON_ID = 'creevey'; // TODO Add `useController` hook // TODO use `import { useGlobals, useStorybookApi } from '@storybook/manager-api';` class CreeveyController { storybookApi; storyId = ''; activeBrowser = ''; selectedTestId = ''; status = { isRunning: false, tests: {}, browsers: [] }; creeveyApi = null; stories = {}; updateStatusListeners = []; changeTestListeners = []; constructor(storybookApi) { this.storybookApi = storybookApi; this.storybookApi = storybookApi; } initAll = async () => { this.storybookApi.on(core_events_1.STORY_RENDERED, this.onStoryRendered); this.storybookApi.on(core_events_1.SET_STORIES, this.onSetStories); this.creeveyApi = await (0, creeveyClientApi_js_1.initCreeveyClientApi)(); this.creeveyApi.onUpdate(this.handleCreeveyUpdate); this.status = await this.creeveyApi.status; }; onUpdateStatus(listener) { this.updateStatusListeners.push(listener); return () => void (this.updateStatusListeners = this.updateStatusListeners.filter((x) => x != listener)); } onChangeTest(listener) { this.changeTestListeners.push(listener); return () => void (this.changeTestListeners = this.changeTestListeners.filter((x) => x != listener)); } handleCreeveyUpdate = (update) => { const { tests, removedTests = [], isRunning } = update; if ((0, types_js_1.isDefined)(isRunning)) { this.status.isRunning = isRunning; } if ((0, types_js_1.isDefined)(tests)) { const prevTests = this.status.tests; const prevStories = this.stories; Object.values(tests) .filter(types_js_1.isDefined) .forEach((update) => { const { id, skip, status, results, approved, storyId } = update; const test = prevTests[id]; if (!test) { return (prevTests[id] = update); } if ((0, types_js_1.isDefined)(skip)) test.skip = skip; if ((0, types_js_1.isDefined)(status)) { test.status = status; if ((0, types_js_1.isDefined)(storyId) && (0, types_js_1.isDefined)(prevStories[storyId])) { const story = prevStories[storyId]; const storyStatus = this.getStoryTests(storyId); const oldStatus = storyStatus .map((x) => (x.id === id ? status : x.status)) .reduce((oldStatus, newStatus) => (0, helpers_js_1.calcStatus)(oldStatus, newStatus), undefined); story.name = this.addStatusToStoryName(story.name, (0, helpers_js_1.calcStatus)(oldStatus, status), skip ?? false); } } if ((0, types_js_1.isDefined)(results)) { if (test.results) test.results.push(...results); else test.results = results; } if ((0, types_js_1.isDefined)(approved)) { Object.entries(approved).forEach(([image, retry]) => retry !== undefined && ((test.approved = test.approved ?? {})[image] = retry)); } }); const nextTests = {}; const testsToRemove = new Set(removedTests.map(({ id }) => id)); for (const id in prevTests) { if (testsToRemove.has(id)) continue; nextTests[id] = prevTests[id]; } this.status.tests = nextTests; this.stories = prevStories; this.setPanelsTitle(); // TODO Check setStories method in 6.x and migrate properly this.storybookApi.emit(core_events_1.SET_STORIES, this.stories); } this.updateStatusListeners.forEach((x) => { x(update); }); }; getCurrentTest = () => { return this.status.tests[this.selectedTestId]; }; onStoryRendered = (storyId) => { if (this.storyId === '') this.addStatusesToSideBar(); if (this.storyId !== storyId) { this.storyId = storyId; this.selectedTestId = this.getTestsByStoryIdAndBrowser(this.activeBrowser)[0]?.id ?? ''; this.setPanelsTitle(); this.changeTestListeners.forEach((x) => { x(this.selectedTestId); }); } }; onStart = () => { this.creeveyApi?.start([this.selectedTestId]); }; onStop = () => { this.creeveyApi?.stop(); }; onImageApprove = (id, retry, image) => this.creeveyApi?.approve(id, retry, image); onStartAllStoryTests = () => { const ids = Object.values(this.status.tests) .filter(types_js_1.isDefined) .filter((x) => x.storyId === this.storyId) .map((x) => x.id); this.creeveyApi?.start(ids); }; onStartAllTests = () => { const ids = Object.values(this.status.tests) .filter(types_js_1.isDefined) .map((x) => x.id); this.creeveyApi?.start(ids); }; onSetStories = (data) => { const stories = data.v ? (0, index_js_1.denormalizeStoryParameters)(data) : data.stories; this.stories = stories; }; setActiveBrowser = (browser) => { this.activeBrowser = browser; this.selectedTestId = this.getTestsByStoryIdAndBrowser(this.activeBrowser)[0]?.id ?? ''; this.changeTestListeners.forEach((x) => { x(this.selectedTestId); }); }; setSelectedTestId = (testId) => { this.selectedTestId = testId; this.changeTestListeners.forEach((x) => { x(this.selectedTestId); }); }; getStoryTests = (storyId) => { return Object.values(this.status.tests) .filter((result) => result?.storyId === storyId) .filter(types_js_1.isDefined); }; getBrowsers = () => { return this.status.browsers; }; getTestsByStoryIdAndBrowser = (browser) => { return Object.values(this.status.tests) .filter((x) => x?.browser === browser && x.storyId === this.storyId) .filter(types_js_1.isDefined); }; getTabTitle = (browser) => { const tests = this.getTestsByStoryIdAndBrowser(browser); const browserStatus = tests .map((x) => x.status) .reduce((oldStatus, newStatus) => (0, helpers_js_1.calcStatus)(oldStatus, newStatus), undefined); const browserSkip = tests.length > 0 ? tests.every((x) => x.skip) : false; const emojiStatus = (0, utils_js_1.getEmojiByTestStatus)(browserStatus, browserSkip); return `${emojiStatus ? `${emojiStatus} ` : ''}Creevey/${browser}`; }; setPanelsTitle = () => { const panels = this.storybookApi.getElements(types_1.Addon_TypesEnum.PANEL); let firstPanelBrowser = this.activeBrowser; for (const p in panels) { const panel = panels[p]; if (panel.id?.indexOf(exports.ADDON_ID) === 0 && panel.paramKey) { panel.title = this.getTabTitle(panel.paramKey); if (!firstPanelBrowser) firstPanelBrowser = panel.paramKey; } } this.storybookApi.setSelectedPanel(`${exports.ADDON_ID}/panel/${firstPanelBrowser}`); }; addStatusesToSideBar() { if (!Object.keys(this.stories).length) return; const stories = this.stories; Object.keys(this.stories).forEach((storyId) => { const storyStatus = this.getStoryTests(storyId); const status = storyStatus .map((x) => x.status) .reduce((oldStatus, newStatus) => (0, helpers_js_1.calcStatus)(oldStatus, newStatus), undefined); const skip = storyStatus.length > 0 ? storyStatus.every((x) => x.skip) : false; this.stories[storyId].name = this.addStatusToStoryName(stories[storyId].name, status, skip); }); // TODO Check setStories method in 6.x and migrate properly this.storybookApi.emit(core_events_1.SET_STORIES, this.stories); } addStatusToStoryName(name, status, skip) { name = name.replace(/^(❌|✔|🟡|🕗|⏸) /, ''); const emojiStatus = (0, utils_js_1.getEmojiByTestStatus)(status, skip); return `${emojiStatus ? `${emojiStatus} ` : ''} ${name}`; } } exports.CreeveyController = CreeveyController; //# sourceMappingURL=controller.js.map