-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Возможность кастомно генерировать автоскриншоты #25
Conversation
@@ -68,6 +69,11 @@ export function parseConfig(options: PluginPartialConfig): PluginConfig { | |||
enabled: booleanOption("enabled", true), | |||
storybookConfigDir: stringOption("storybookConfigDir", ".storybook"), | |||
autoScreenshots: booleanOption("autoScreenshots", true), | |||
customAutoScreenshots: map(section({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
тут быстро не смог разобраться, как сделать так, чтобы в globals произвольный объект мог быть
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { isNull, isPlainObject } from "lodash";
const assertOptionalObject = (value, name) => {
if (!isNull(value) && !isPlainObject(value)) {
throw new Error(`"${name}" must be an object`);
}
};
const optionalObject = (name, defaultValue) =>
option({
parseEnv: JSON.parse,
parseCli: JSON.parse,
defaultValue,
validate: value => assertOptionalObject(value, name),
});
Дефолтное значение лучше поставить {}
, но тогда в src/storybook/story-test-runner/index.ts
проверять нужно будет не if (customAutoScreenshots)
, а if (!isEmpty(customAutoScreenshots))
, где isEmpty
из "lodash"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я бы вовсе сделал так, чтобы тип этого параметра был Record<string, Record<string, unknown>>
. То есть, убрал этот лишний уровень вложенности с globals
, потому что не имеет смысла тут в конфиге пушить пользователя описывать globals
, а затем вытаскивать только этот globals
, игнорируя все остальное. Если это какой-то задел на будущее, предлагаю подумать, что еще можно было бы в будущем там описывать, помимо globals
. Если нет - предлагаю убрать
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Если же убрать этот уровень вложенности с globals
, можно будет выбрать название для опции в конфиге поудачнее. Например, autoScreenshotStorybookGlobals
.
customAutoScreenshots
мне не нравится во первых тем, что слишком размыто и из названия вообще непонятно, что может из себя представлять эта опция.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Выглядит прикольно.
Я бы еще поразмышлял: "может ли пользователю понадобиться иметь разные глобалы для разных историй?". Например, может ли пользователь хотеть тестировать один сторибук файл в светлой и темной теме, а другой - в двух разных локалях?
И нужно не забыть описать опцию в документации: https://github.com/gemini-testing/testplane-storybook?tab=readme-ov-file#testplane
return browser.execute(async (globals) => { | ||
const channel = (window as StorybookWindow).__STORYBOOK_ADDONS_CHANNEL__; | ||
channel.emit("updateGlobals", { globals }); | ||
}, globals); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Вот тут не нужен async
@@ -68,6 +69,11 @@ export function parseConfig(options: PluginPartialConfig): PluginConfig { | |||
enabled: booleanOption("enabled", true), | |||
storybookConfigDir: stringOption("storybookConfigDir", ".storybook"), | |||
autoScreenshots: booleanOption("autoScreenshots", true), | |||
customAutoScreenshots: map(section({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { isNull, isPlainObject } from "lodash";
const assertOptionalObject = (value, name) => {
if (!isNull(value) && !isPlainObject(value)) {
throw new Error(`"${name}" must be an object`);
}
};
const optionalObject = (name, defaultValue) =>
option({
parseEnv: JSON.parse,
parseCli: JSON.parse,
defaultValue,
validate: value => assertOptionalObject(value, name),
});
Дефолтное значение лучше поставить {}
, но тогда в src/storybook/story-test-runner/index.ts
проверять нужно будет не if (customAutoScreenshots)
, а if (!isEmpty(customAutoScreenshots))
, где isEmpty
из "lodash"
@@ -68,6 +69,11 @@ export function parseConfig(options: PluginPartialConfig): PluginConfig { | |||
enabled: booleanOption("enabled", true), | |||
storybookConfigDir: stringOption("storybookConfigDir", ".storybook"), | |||
autoScreenshots: booleanOption("autoScreenshots", true), | |||
customAutoScreenshots: map(section({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я бы вовсе сделал так, чтобы тип этого параметра был Record<string, Record<string, unknown>>
. То есть, убрал этот лишний уровень вложенности с globals
, потому что не имеет смысла тут в конфиге пушить пользователя описывать globals
, а затем вытаскивать только этот globals
, игнорируя все остальное. Если это какой-то задел на будущее, предлагаю подумать, что еще можно было бы в будущем там описывать, помимо globals
. Если нет - предлагаю убрать
@@ -68,6 +69,11 @@ export function parseConfig(options: PluginPartialConfig): PluginConfig { | |||
enabled: booleanOption("enabled", true), | |||
storybookConfigDir: stringOption("storybookConfigDir", ".storybook"), | |||
autoScreenshots: booleanOption("autoScreenshots", true), | |||
customAutoScreenshots: map(section({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Если же убрать этот уровень вложенности с globals
, можно будет выбрать название для опции в конфиге поудачнее. Например, autoScreenshotStorybookGlobals
.
customAutoScreenshots
мне не нравится во первых тем, что слишком размыто и из названия вообще непонятно, что может из себя представлять эта опция.
Я тут подумал, может вообще немного по-другому сделать? "@testplane/storybook": {
remoteStorybookUrl: STORYBOOK_URL,
defaultTests: {
'Authscreenshot default': (ctx: TestFunctionExtendedCtx) => {
ctx.browser.assertView(...)
},
'Autoscreenshot first theme': (ctx: TestFunctionExtendedCtx) => {
//set first theme
ctx.browser.execute(...)
ctx.browser.assertView(...)
},
'Autoscreenshot second theme': (ctx: TestFunctionExtendedCtx) => {
//set second theme
ctx.browser.execute(...)
ctx.browser.assertView(...)
},
}
} Так получается более гибко и логично, + интерфейс такой же, как у extraTests, каждый сможет делать все, что хочет(конкретно мы у себя сами проставим globals) + это можно будет переопределять на уровне конкретных сториков |
Мне тут не нравится, что пользователь сам вызывает В таком случае было бы логичнее описывать "действия до скриншота", чтобы пользователю не нужно было самому этот скриншот вызывать, но:
Поэтому я бы предпочел упростить, насколько возможно, чтобы пользователь просто описывал сеты проверок с разными глобалами. |
Я еще раз посмотрел на наши тесты, у нас везде помимо установки темы еще докидываются разные стили, поэтому, кажется, autoScreenshotStorybookGlobals - такое не подходит, так как не понятно куда их докидывать, а способ с defaultTests дает максимальную гибкость |
Покажи пожалуйста пример. Что за стили и как они докидываются? |
describe('HeaderV2', function () {
it('Второй уровень вложенности в HeaderV2 десктоп', async function () {
await this.browser.selectStory('header--template');
await this.browser.setGlobals({ theme: 'dark' });
await this.browser.addStyles('#storybook-root', { background: 'black' });
await this.browser.$('//button[starts-with(@data-testid,"AccentDropdown-StyledButton")]//*[contains(text(), "Обучение")]').click();
await this.browser.assertView('HeaderV2Level2Desktop', '#storybook-root');
});
it('Третий уровень вложенности в HeaderV2 десктоп', async function () {
await this.browser.selectStory('header--template');
await this.browser.setGlobals({ theme: 'light' });
await this.browser.addStyles('#storybook-root', { background: 'light-blue' });
await this.browser.$('//div[starts-with(@data-testid,"MenuDropdownSecondNav-RelativeDiv")]//*[contains(text(), "Сертификация")]').click();
await this.browser.assertView('HeaderV2Level3Desktop', '#storybook-root');
});
}); browser.addCommand(
'addStyles',
function (selector, styles) {
this.execute((selector, styles, styleId) => {
let styleElement = document.getElementById(styleId);
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.id = styleId;
document.getElementsByTagName('head')[0].appendChild(styleElement);
}
styleElement.appendChild(document.createTextNode(`${selector} {${Object.entries(styles).map(([key, value]) => `${key}: ${value};`).join(' ')}}`));
}, selector, styles, STYLE_ID);
}
); и так во всех тестах - для каждой темы накидывается стиль |
Для чего у |
Клик перед
|
в команде сказали, что им так проще смотреть диффы в большинстве случаев на данных фонах. |
Это я просто скопировал рандомные тесты, не имел в виду, что нужна возможность настраивать клик через конфиг плагина |
Посыл был примерно такой: autoScreenshotStorybookGlobals - может не масштабироваться в будущем, т.к. не понятно, что делать если появятся какие-то доп условия. Мне намного больше нравится вариант с defaultTests, т.к. это просто способ изменить вот это значение по-умолчанию https://github.com/gemini-testing/testplane-storybook/blob/master/src/storybook/story-test-runner/index.ts#L22-L28 и нет ничего страшного в том, чтобы в конфиге один раз написать не 5 строчек, а 10. |
Если хочется максимальной масштабируемости и возможности самостоятельно писать такие тесты и многие другие, советую посмотреть в эту сторону: https://github.com/gemini-testing/testplane-storybook/?tab=readme-ov-file#advanced-usage Загрузка истории будет выполняться на стороне import defaultTestplaneTests from "...";
import type { StoryObj } from "@storybook/react";
import type { WithTestplane } from "@testplane/storybook"
export const Primary: WithTestplane<StoryObj> = {
args: {
primary: true,
label: "Button",
},
testplane: defaultTestplaneTests,
};
export const Secondary: WithTestplane<StoryObj> = {
args: {
primary: false,
label: "Button",
},
testplane: {
...defaultTestplaneTests,
"my test": async ({browser, expect}) => {
const element = await browser.$(".storybook-button");
await expect(element).toHaveText("Button");
}
},
}; Это гибко и масштабируемо, потому что можно описывать тесты для каждой стори отдельно от других. Если же просто хочется поменять фон сторибука, то для этого не нужно изменять дефолтные testplane тесты. Можно сделать это средствами самого сторибука: их можно указать как на глобальном уровне, на уровне компонента и на уровне стори: https://storybook.js.org/docs/writing-stories/parameters#global-parameters Еще можно написать декоратор: https://storybook.js.org/docs/writing-stories/decorators#global-decorators Или можно использовать сторибук аддон backgrounds: https://storybook.js.org/docs/essentials/backgrounds
|
Конкретно сейчас мне хочется тестировать автоматически компоненты в двух темах(чтобы генерировалось 2 автоскриншотных теста, а не 1) - сейчас такой возможности нет. Про advanced-usage я в курсе, но для этого будет нужно в каждой истории писать эти дополнительные тесты |
Вот это немного не понял, параметр defaultTests ничего не ломает же, параметр skip для него будет работать так же как и раньше - просто не сгенерируются тесты |
В общем, идея с добавлением в конфиг чего-то типа "autoScreenshotStorybookGlobals", чтобы из одной стори делать сразу несколько скриншотных тестов нам нравится. Вижу, в коде по ключу с "названием теста" есть объект с одним полем: Касаемо доп. стилей: изменения цвета фона можно (и лучше) добиться с помощью аддонов под сторибук. Про По этому PR: мы можем сами закончить работу над этой фичей (с возможностью декларативно указывать разные сеты глобалов), нам так, наверное, будет проще. Или есть сильное желание сделать это самостоятельно? |
У меня нет, у меня не очень много опыта со сторибуком, поэтому я скорее перестраховался для будущего возможного расширения.
Если вам так будет проще, то давайте так, это непринципиально |
Доработал PR с твоим коммитом в #28, этот закрываю. Спасибо за контрибьют! |
Отлично! Спасибо и вам |
Released as @testplane/[email protected] |
"@testplane/storybook": { remoteStorybookUrl: STORYBOOK_URL, customAutoScreenshots: { 'Authscreenshot default': {}, 'Autoscreenshot light theme': { globals: {theme: 'light'}}, 'Autoscreenshot dark theme': { globals: {theme: 'dark'}}, } }