Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/common-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const {
RUNNING,
QUEUED
} = require('./constants/test-statuses');
const {UNCHECKED, INDETERMINATE, CHECKED} = require('./constants/checked-statuses');

exports.getShortMD5 = (str) => {
return crypto.createHash('md5').update(str, 'ascii').digest('hex').substr(0, 7);
Expand Down Expand Up @@ -115,3 +116,8 @@ function isRelativeUrl(url) {
return true;
}
}

exports.isCheckboxChecked = (status) => status == CHECKED; // eslint-disable-line eqeqeq
exports.isCheckboxIndeterminate = (status) => status == INDETERMINATE; // eslint-disable-line eqeqeq
exports.isCheckboxUnchecked = (status) => status == UNCHECKED; // eslint-disable-line eqeqeq
exports.getToggledCheckboxState = (status) => exports.isCheckboxChecked(status) ? UNCHECKED : CHECKED;
7 changes: 7 additions & 0 deletions lib/constants/checked-statuses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

module.exports = {
UNCHECKED: 0,
INDETERMINATE: 0.5,
CHECKED: 1
};
Comment on lines +3 to +7
Copy link
Member Author

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. Тип - число, потому что так удобнее считать.

30 changes: 30 additions & 0 deletions lib/static/components/bullet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import classNames from 'classnames';
import {Checkbox} from 'semantic-ui-react';
import PropTypes from 'prop-types';
import {isCheckboxChecked, isCheckboxIndeterminate} from '../../common-utils';
import {CHECKED, INDETERMINATE, UNCHECKED} from '../../constants/checked-statuses';
import useLocalStorage from '../hooks/useLocalStorage';

const Bullet = ({status, onClick, className}) => {
const [isCheckbox] = useLocalStorage('showCheckboxes', false);

if (!isCheckbox) {
return <span className={classNames('bullet_type-simple', className)} />;
}

return <Checkbox
className={classNames('bullet_type-checkbox', className)}
checked={isCheckboxChecked(status)}
indeterminate={isCheckboxIndeterminate(status)}
onClick={onClick}
/>;
};

Bullet.propTypes = {
status: PropTypes.oneOf([CHECKED, UNCHECKED, INDETERMINATE]),
onClick: PropTypes.func,
bulletClassName: PropTypes.string
};

export default Bullet;
6 changes: 4 additions & 2 deletions lib/static/components/controls/common-filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import {connect} from 'react-redux';
import * as actions from '../../modules/actions';
import TestNameFilterInput from './test-name-filter-input';
import StrictMatchFilterInput from './strict-match-filter-input';
import ShowCheckboxesInput from './show-checkboxes-input';
import BrowserList from './browser-list';

class CommonFilters extends Component {
render() {
const {filteredBrowsers, browsers, actions} = this.props;
const {filteredBrowsers, browsers, gui, actions} = this.props;

return (
<div className="control-container control-filters">
Expand All @@ -21,12 +22,13 @@ class CommonFilters extends Component {
/>
<TestNameFilterInput/>
<StrictMatchFilterInput/>
{gui && <ShowCheckboxesInput/>}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Затычка, чтобы убрать чекбоксы из статического отчета.

Добавил, потому что пока от этих чекбоксов в статическом отчете никакого толку нет.

Чтобы чекбоксы в статике заработали, достаточно просто убрать вот это вот условие

</div>
);
}
}

export default connect(
({view, browsers}) => ({filteredBrowsers: view.filteredBrowsers, browsers}),
({view, browsers, gui}) => ({filteredBrowsers: view.filteredBrowsers, browsers, gui}),
(dispatch) => ({actions: bindActionCreators(actions, dispatch)})
)(CommonFilters);
43 changes: 4 additions & 39 deletions lib/static/components/controls/gui-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,23 @@ import ControlButton from './control-button';
import RunButton from './run-button';
import AcceptOpenedButton from './accept-opened-button';
import CommonFilters from './common-filters';
import {getFailedTests} from '../../modules/selectors/tree';

import './controls.less';

class GuiControls extends Component {
static propTypes = {
// from store
running: PropTypes.bool.isRequired,
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);
Comment on lines -20 to -34
Copy link
Member Author

@KuznetsovRoman KuznetsovRoman Jan 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот это вот все унес под кнопку <RunButton />

stopping: PropTypes.bool.isRequired
}

render() {
const {actions, allRootSuiteIds, failedRootSuiteIds, running, autoRun, processing, stopping} = this.props;
const {actions, running, stopping} = this.props;
return (
<div className="main-menu container">
<CustomGuiControls />
<div className="control-container control-buttons">
<RunButton
autoRun={autoRun}
isDisabled={!allRootSuiteIds.length || processing}
handler={actions.runAllTests}
isRunning={running}
/>
<ControlButton
label="Retry failed tests"
isDisabled={!failedRootSuiteIds.length || processing}
handler={this._runFailedTests}
/>
<RunButton />
<ControlButton
label="Stop tests"
isDisabled={!running || stopping}
Expand All @@ -66,16 +41,6 @@ class GuiControls extends Component {
}

export default connect(
(state) => {
return {
running: state.running,
processing: state.processing,
stopping: state.stopping,
autoRun: state.autoRun,
allRootSuiteIds: state.tree.suites.allRootIds,
failedRootSuiteIds: state.tree.suites.failedRootIds,
failedTests: getFailedTests(state)
};
},
({running, stopping}) => ({running, stopping}),
(dispatch) => ({actions: bindActionCreators(actions, dispatch)})
)(GuiControls);
27 changes: 0 additions & 27 deletions lib/static/components/controls/run-button.js

This file was deleted.

129 changes: 129 additions & 0 deletions lib/static/components/controls/run-button/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
'use strict';

import React, {useEffect, useState} from 'react';
import {bindActionCreators} from 'redux';
import {isEmpty} from 'lodash';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import * as actions from '../../../modules/actions';
import Popup from '../../popup';
import {getFailedTests, getCheckedTests} from '../../../modules/selectors/tree';
import useLocalStorage from '../../../hooks/useLocalStorage';

import './index.styl';

const RunMode = Object.freeze({
ALL: 'All',
FAILED: 'Failed',
CHECKED: 'Checked'
});
Comment on lines +16 to +20
Copy link
Member Author

@KuznetsovRoman KuznetsovRoman Jan 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Что-то типа Enum


const RunButton = ({actions, autoRun, isDisabled, isRunning, failedTests, checkedTests}) => {
const [mode, setMode] = useState(RunMode.ALL);
const [showCheckboxes] = useLocalStorage('showCheckboxes', false);

const btnClassName = classNames('btn', {'button_blink': isRunning});

const shouldDisableFailed = isEmpty(failedTests);
const shouldDisableChecked = !showCheckboxes || isEmpty(checkedTests);

const selectAllTests = () => setMode(RunMode.ALL);
const selectFailedTests = () => !shouldDisableFailed && setMode(RunMode.FAILED);
const selectCheckedTests = () => !shouldDisableChecked && setMode(RunMode.CHECKED);

const runAllTests = () => actions.runAllTests();
const runFailedTests = () => actions.runFailedTests(failedTests);
const runCheckedTests = () => actions.retrySuite(checkedTests);

const handleRunClick = () => {
const action = {
[RunMode.ALL]: runAllTests,
[RunMode.FAILED]: runFailedTests,
[RunMode.CHECKED]: runCheckedTests
}[mode];

action();
};

useEffect(() => {
if (autoRun) {
runAllTests();
}
}, []);

useEffect(() => {
selectCheckedTests();
}, [shouldDisableChecked]);

useEffect(() => {
const shouldResetFailedMode = mode === RunMode.FAILED && shouldDisableFailed;
const shouldResetCheckedMode = mode === RunMode.CHECKED && shouldDisableChecked;

if (shouldResetFailedMode || shouldResetCheckedMode) {
setMode(RunMode.ALL);
}
}, [shouldDisableFailed, shouldDisableChecked]);

return (
<div className='run-button'>
<button disabled={isDisabled} onClick={handleRunClick} className={btnClassName}>
{isRunning ? 'Running' : `Run ${mode.toLowerCase()} tests`}
</button>
{!isDisabled && <Popup
action='hover'
hideOnClick={true}
target={<div className='run-button__dropdown' />}
>
<ul className='run-mode'>
<li
className='run-mode__item'
onClick={selectAllTests}
>
{RunMode.ALL}
</li>
<li
className={classNames('run-mode__item', {'run-mode__item_disabled': shouldDisableFailed})}
onClick={selectFailedTests}>{RunMode.FAILED}
</li>
<li
className={classNames('run-mode__item', {'run-mode__item_disabled': shouldDisableChecked})}
onClick={selectCheckedTests}
>
{RunMode.CHECKED}
</li>
</ul>
</Popup>}
</div>
);
};

RunButton.propTypes = {
// from store
autoRun: PropTypes.bool.isRequired,
isDisabled: PropTypes.bool,
isRunning: PropTypes.bool,
failedTests: PropTypes.arrayOf(PropTypes.shape({
testName: PropTypes.string,
browserName: PropTypes.string
})).isRequired,
checkedTests: PropTypes.arrayOf(PropTypes.shape({
testName: PropTypes.string,
browserName: PropTypes.string
})).isRequired
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

но ведь если у нас не включен режим чекбокса, то этого значения не будет. Или будет просто пустой массив?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image Слайдер на чекбокс только переключает его отображение, и по сути выполняет чисто визуальную функцию. Если чекнуть какой-то тест, потом выключить чекбоксы, а потом включить обратно, то `checkedTests` сохранится. Так что тут если чекбоксы выключены, это просто будет пустым массивом

};

export default connect(
(state) => {
const autoRun = state.autoRun;
const allRootSuiteIds = state.tree.suites.allRootIds;
const processing = state.processing;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

чем у нас processing отличается от running?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

processing не обязательно running. processing может быть принятие скриншота

const isDisabled = !allRootSuiteIds.length || processing;
const isRunning = state.running;
const failedTests = getFailedTests(state);
const checkedTests = getCheckedTests(state);

return {autoRun, isDisabled, isRunning, failedTests, checkedTests};
},
(dispatch) => ({actions: bindActionCreators(actions, dispatch)})
)(RunButton);
49 changes: 49 additions & 0 deletions lib/static/components/controls/run-button/index.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.run-button {
display: inline-flex;
align-items: center;
cursor: pointer;
width: 143px;
font-size: 11px;
line-height: 11px;
border: 1px solid #ccc;
border-radius: 2px;
background-color: #ffeba0;

.btn {
flex-grow: 1;
height: 100%;
background-color: #ffeba0;
cursor: pointer;
border: none;
}

.run-button__dropdown {
font-family: Dropdown;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

что это за ff такой?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это специальный шрифт из semantic-ui-react для отображения этой стрелочки у Dropdown:
image

Заиспользовал его, чтобы не плодить лишних svg


&::before {
content: '\f0d7';
padding: 5px;
border-left: 1px solid #ccc;
}
}

.popup__content {
padding: 0;
}

.run-mode {
padding: 0;

.run-mode__item {
font-size: 13px;
padding: 5px 10px;
list-style: none;
user-select: none;

&.run-mode__item_disabled {
color: #939393;
cursor: auto;
}
}
}
}
24 changes: 24 additions & 0 deletions lib/static/components/controls/show-checkboxes-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';

import React from 'react';
import {Checkbox} from 'semantic-ui-react';
import useLocalStorage from '../../hooks/useLocalStorage';

const ShowCheckboxesInput = () => {
const [showCheckboxes, setShowCheckboxes] = useLocalStorage('showCheckboxes', false);

const onChange = () => setShowCheckboxes(!showCheckboxes);

return (
<div className="toggle-control">
<Checkbox
toggle
label="Checkboxes"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

почему это checkboxes, а не checkbox?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лейбл. Это чекбокс с чекбоксАМИ. Будет как то странно видеть чекбокс с лейблом "чекбокс", а вот "чекбоксы" уже несколько понятнее)

onChange={onChange}
checked={showCheckboxes}
/>
</div>
);
};

export default ShowCheckboxesInput;
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const StrictMatchFilterInput = ({strictMatchFilter, actions}) => {
};

return (
<div className="strict-match-filter">
<div className="toggle-control">
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Заменил название класса, чтобы заюзать этот же класс для <ShowCheckboxesInput/>

<Checkbox
toggle
label="Strict match"
Expand Down
Loading