-
Notifications
You must be signed in to change notification settings - Fork 39
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
feat: retry tests with checkboxes #453
Conversation
0786350
to
fb59b5e
Compare
ce3b443
to
8e8ad44
Compare
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.
Что сделано
Добавил чекбоксы на:
- браузеры
- сьюты
- группы
У чекбоксов три состояния: "Выбрано", "Не выбрано", "Выбрано частично"
Чекбоксы отжимаются, когда изменяется режим просмотра (фильтр по имени теста, по браузеру, по статусу, группировка), также обновляется статус чекбоксов (ex: полу-выбранный может стать выбранным, если в текущем режиме просмотра все его дети выбраны)
Также изменил кнопку запуска, теперь в ней совмещены три режима: "Запустить (все|выбранные|проваленные) тесты"
Принятые решения
Отключил чекбоксы пока в статике, так как там они не делают ничего.
Скриншоты
@@ -21,12 +22,13 @@ class CommonFilters extends Component { | |||
/> | |||
<TestNameFilterInput/> | |||
<StrictMatchFilterInput/> | |||
{gui && <ShowCheckboxesInput/>} |
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.
Затычка, чтобы убрать чекбоксы из статического отчета.
Добавил, потому что пока от этих чекбоксов в статическом отчете никакого толку нет.
Чтобы чекбоксы в статике заработали, достаточно просто убрать вот это вот условие
processing: PropTypes.bool.isRequired, | ||
stopping: PropTypes.bool.isRequired, | ||
autoRun: PropTypes.bool.isRequired, | ||
allRootSuiteIds: PropTypes.arrayOf(PropTypes.string).isRequired, | ||
failedRootSuiteIds: PropTypes.arrayOf(PropTypes.string).isRequired, | ||
failedTests: PropTypes.arrayOf(PropTypes.shape({ | ||
testName: PropTypes.string, | ||
browserName: PropTypes.string | ||
})).isRequired | ||
} | ||
|
||
_runFailedTests = () => { | ||
const {actions, failedTests} = this.props; | ||
|
||
return actions.runFailedTests(failedTests); |
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.
Вот это вот все унес под кнопку <RunButton />
const RunMode = Object.freeze({ | ||
ALL: 'All', | ||
FAILED: 'Failed', | ||
CHECKED: 'Checked' | ||
}); |
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.
Что-то типа Enum
const shouldShowFailed = !!failedTests.length; | ||
const shouldShowChecked = showCheckboxes && !!checkedTests.length; | ||
const shouldShowPopup = !isDisabled && (shouldShowFailed || shouldShowChecked); |
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.
Отображаем:
- вариант
Failed
если упавшие тесты есть - вариант
Checked
если выбран соответствующий режим и есть выбранные тесты - сам попап, если помимо
All
доступен хоть один вариант выше
useEffect(() => { | ||
const pickedEmptyFailed = mode === RunMode.FAILED && !shouldShowFailed; | ||
const pickedEmptyChecked = mode === RunMode.CHECKED && !shouldShowChecked; | ||
|
||
if (pickedEmptyFailed || pickedEmptyChecked) { | ||
setMode(RunMode.ALL); | ||
} | ||
}, [shouldShowFailed, shouldShowChecked]); |
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.
Скидываем режим до All
, если текущий выбранный вариант пропал. Такое бывает, если:
- Перезапустили упавшие тесты, и они прошли
- Отключили чекбоксы
- Анчекнули все тесты
export function updateParentsChecked(tree, parentIds) { | ||
const youngerSuites = [].concat(parentIds) | ||
.filter(parentId => parentId) | ||
.map((suiteId) => tree.suites.byId[suiteId]); | ||
|
||
const changeParentSuiteCb = (parentSuite) => { | ||
changeSuiteState(tree, parentSuite.id, {shouldBeChecked: shouldSuiteBeChecked(parentSuite, tree)}); | ||
}; | ||
|
||
youngerSuites.forEach(changeParentSuiteCb); | ||
|
||
calcParentSuitesState(youngerSuites, tree, changeParentSuiteCb); | ||
} |
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.
Заиспользовал имеющуюся функцию calcParentSuitesState
, но первый слой сьютов, который имеет браузеры, оно не обрабатывает, поэтому по ним проходимся вручную
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.
UPD: shouldBeChecked
-> checkStatus
@@ -33,6 +35,20 @@ export function updateSuitesStatus(tree, suites) { | |||
}); | |||
} | |||
|
|||
export function updateParentsChecked(tree, parentIds) { | |||
const youngerSuites = [].concat(parentIds) | |||
.filter(parentId => parentId) |
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.
До этого фильтра тут еще могут оказаться null
-ы
return checkedChildCount && checkedChildCount < childCount | ||
? undefined | ||
: checkedChildCount === childCount; |
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.
Если выбрано не нулевое количество детей, но не все, то ставим сьюту undefined
.
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.
UPD:
return Number((checkedChildCount === childCount) || (checkedChildCount && INDETERMINATE));
width: 16px; | ||
width: 18px; |
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.
Чекбоксы изначально 17х17. Чтобы не дергалось при их включении/выключании, поставил всем ширину в 18 пикселей.
describe('skipped test', () => { | ||
it('should render "[skipped]" tag in title', () => { | ||
it('should pass "[skipped]" tag in title', () => { | ||
const browsersById = mkBrowser({id: 'yabro-1', name: 'yabro', resultIds: ['res'], parentId: 'test'}); | ||
const browsersStateById = {'yabro-1': {shouldBeShown: true, shouldBeOpened: false}}; | ||
const resultsById = mkResult({id: 'res', status: SKIPPED}); | ||
const tree = mkStateTree({browsersById, browsersStateById, resultsById}); | ||
|
||
const component = mkSectionBrowserComponent({browserId: 'yabro-1'}, {tree}); | ||
mkSectionBrowserComponent({browserId: 'yabro-1'}, {tree}); | ||
|
||
assert.equal(component.find('.section__title_skipped').first().text(), `[${SKIPPED}] yabro`); | ||
assert.calledWithMatch( | ||
BrowserSkippedTitle, | ||
set({}, 'title.props.children', [`[${SKIPPED}] `, 'yabro', undefined, undefined]) | ||
); | ||
}); |
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.
В этом файле тестировался по сути <BrowserTitle/>
компонент.
Это мешало, потому что <BrowserTitle/>
теперь обращается к localStorage
по хуку useLocalStorage
, и тут нужно было либо перестать тестировать ребенка, либо передавать сюда в proxyquire
<BrowserTitle/>
с застабанным useLocalStorage
. Выбрал первый вариант, кажется, так правильнее. Тесты ребенка перенес в отдельный файл, а тут застабал и проверяю, с каким тайтлом он вызывается.
Там в качестве title
- React.Fragment
, поэтому через массив:
['[skipped]', <browser>, ' reason ', <reason>]
8e8ad44
to
79fd55e
Compare
79fd55e
to
01ff6ce
Compare
module.exports = { | ||
UNCHECKED: 0, | ||
INDETERMINATE: 0.5, | ||
CHECKED: 1 | ||
}; |
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.
Сначала были checked
/ unchecked
- true
/ false
, соответственно. Потом появились indeterminate
. Булевых значений всего два, поэтому и есть этот enum
. Тип - число, потому что так удобнее считать.
208688c
to
215bdf2
Compare
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.
последние тесты на редьюсеры получились прям сложными (но я сходу не придумал как их упростить)
Еще режет глаз постоянные Number(!checkStatus)
. очень легко запутаться
в остальном норм (но я еще в живую потыкаю)
upd: в попапе не хватает разделителей между режимами. сейчас слова как будто подвешены в воздухе
} | ||
|
||
.run-button__dropdown { | ||
font-family: Dropdown; |
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.
что это за ff такой?
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.
{shouldShowPopup && <Popup | ||
action='hover' | ||
hideOnClick={true} | ||
target={<span className='run-button__dropdown' />} |
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.
для чего это делается span-ом?
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.
span
по своей натуре строчный элемент, в то время как div
- блочный. Может с div
отображалось бы так же, но как будто семантически ближе именно span
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.
span как раз больше для выделения конкретных частей текста (например, подсветить слово)
div будет вести себя так же. Но не критично
checkedTests: PropTypes.arrayOf(PropTypes.shape({ | ||
testName: PropTypes.string, | ||
browserName: PropTypes.string | ||
})).isRequired |
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.
но ведь если у нас не включен режим чекбокса, то этого значения не будет. Или будет просто пустой массив?
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.
(state) => { | ||
const autoRun = state.autoRun; | ||
const allRootSuiteIds = state.tree.suites.allRootIds; | ||
const processing = state.processing; |
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.
чем у нас processing отличается от running?
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.
processing
не обязательно running
. processing
может быть принятие скриншота
useLocalStorageStub = sandbox.stub().returns([true]); | ||
actionsStub = { | ||
runAllTests: sandbox.stub().returns({type: 'some-type'}), | ||
runFailedTests: sandbox.stub().returns({type: 'some-type'}), |
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.
зачем возвращать одинаковые значения для разных экшенов?
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.
Нам абсолютно нет никакой разницы, что там за тип, главное, чтобы он был
В тестах нет никакой завязки на эти type
, проверяется именно, что вызывается сама функция runAllTests
, runFailedTests
. Ну и такое возвращаемое значение застабано для всех экшенов в тестах: https://github.com/search?q=repo%3Agemini-testing%2Fhtml-reporter%20type%3A%20%27some-type%27&type=code
const state = mkState({initialState: {tree: {suites: {allRootIds: ['suite']}}, processing: false}}); | ||
selectorsStub.getFailedTests.withArgs(state).returns(failedTests); | ||
const component = mkConnectedComponent(<RunButton />, {state}); | ||
component.findWhere(node => node.type() === 'li' && node.text() === 'Failed').simulate('click'); |
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.
а че, по value нельзя проще найти? или добавить data-атрибут в разметку
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.
Не знал, что так можно. Заменил на component.find({children: 'Failed'}).simulate('click');
|
||
const component = mkGroupTestsItemComponent(); | ||
|
||
assert.equal(component.find(Checkbox).exists(), +show); |
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.
чет я не понял. exists же булевое значение вернет
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.
А зачем я тогда show
к числу привел? Пофиксил
test/unit/lib/static/components/section/title/browser-skipped.js
Outdated
Show resolved
Hide resolved
}).default; | ||
}); | ||
|
||
describe('<Checkbox/>', () => { | ||
[true, false].forEach(show => { | ||
it(`should ${show ? '' : 'not '}exist if "showCheckboxes" is ${show ? '' : 'not '}set`, () => { |
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.
выше уже писал, что стОит вынести логику в общий компонент. у тебя и тесты дублируются теперь
Я бы еще в попапе не удалял пункты, которых не может быть, а дизхейблил. Так будет сильно проще пользователю, т.к. он привыкает, что вторая строчка - это перезапуск упавших тестов, а там, вдруг, уже перезапуск выделенных тестов. Интерфейс должен оставаться постоянным |
50341a0
to
a9b50e3
Compare
Попробуй в Визуально оно так выглядит, потому что Устанавливается он как раз через |
233cc01
to
4af63cd
Compare
c0e184e
to
a3cb944
Compare
@@ -53,28 +53,47 @@ const RunButton = ({actions, autoRun, isDisabled, isRunning, failedTests, checke | |||
}, []); | |||
|
|||
useEffect(() => { | |||
const pickedEmptyFailed = mode === RunMode.FAILED && !shouldShowFailed; | |||
const pickedEmptyChecked = mode === RunMode.CHECKED && !shouldShowChecked; | |||
if (!shouldDisableChecked) { |
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.
у тебя здесь получается двойная проверка на shouldDisableChecked - сначала здесь, а потом и внутри функции selectCheckedTests
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.
Убрал внутри useEffect
, потому что disabled
опции все равно могут получить клик
{shouldShowPopup && <Popup | ||
action='hover' | ||
hideOnClick={true} | ||
target={<span className='run-button__dropdown' />} |
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.
span как раз больше для выделения конкретных частей текста (например, подсветить слово)
div будет вести себя так же. Но не критично
91b28e8
to
9264ff0
Compare
9264ff0
to
d3190c1
Compare
No description provided.