Skip to content

Commit

Permalink
feat: allow to share specific test
Browse files Browse the repository at this point in the history
  • Loading branch information
rmdm committed Dec 27, 2019
1 parent 80275d7 commit 1d83322
Show file tree
Hide file tree
Showing 15 changed files with 463 additions and 74 deletions.
13 changes: 9 additions & 4 deletions lib/static/components/section/body/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ class Body extends Component {
constructor(props) {
super(props);

const retry = typeof this.props.retryIndex === 'number'
? Math.min(this.props.retryIndex, this.props.retries.length)
: this.props.retries.length;

this.state = {
color: 1,
retry: this.props.retries.length
retry
};
}

Expand Down Expand Up @@ -179,7 +183,7 @@ class Body extends Component {
<div className="controls">
<div className="controls__item">
<SwitcherStyle onChange={this.onSwitcherStyleChange}/>
<SwitcherRetry onChange={this.onSwitcherRetryChange} testResults={retries.concat(result)} />
<SwitcherRetry onChange={this.onSwitcherRetryChange} retryIndex={this.state.retry} testResults={retries.concat(result)} />
</div>
{this._addRetryButton()}
</div>
Expand All @@ -193,11 +197,12 @@ class Body extends Component {
}

export default connect(
({gui, running, suites, suiteIds, apiValues}) => ({
({gui, running, suites, suiteIds, apiValues, view: {retryIndex}}) => ({
failed: values(pick(suites, suiteIds.failed)),
gui,
running,
apiValues
apiValues,
retryIndex
}),
(dispatch) => ({actions: bindActionCreators(actions, dispatch)})
)(Body);
2 changes: 1 addition & 1 deletion lib/static/components/section/section-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class SectionBrowser extends Component {
? <BrowserSkippedTitle title={title} />
: (
<Fragment>
<BrowserTitle title={title} result={result} handler={this._onToggleSection} />
<BrowserTitle title={title} result={result} suite={suite} browser={browser} handler={this._onToggleSection} />
{body}
</Fragment>
);
Expand Down
5 changes: 3 additions & 2 deletions lib/static/components/section/switcher-retry.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import classNames from 'classnames';
export default class SwitcherRetry extends Component {
static propTypes = {
testResults: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired
onChange: PropTypes.func.isRequired,
retryIndex: PropTypes.number.isRequired
}

constructor(props) {
super(props);
this.state = {retry: this.props.testResults.length - 1};
this.state = {retry: this.props.retryIndex};
}

render() {
Expand Down
24 changes: 23 additions & 1 deletion lib/static/components/section/title/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@

import url from 'url';
import React, {Component} from 'react';
import ClipboardButton from 'react-clipboard.js';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {appendQuery} from '../../../modules/query-params';

class BrowserTitle extends Component {
static propTypes = {
title: PropTypes.node.isRequired,
result: PropTypes.object.isRequired,
suite: PropTypes.object.isRequired,
browser: PropTypes.object.isRequired,
handler: PropTypes.func.isRequired,
parsedHost: PropTypes.object
}

render() {
const {title, result, handler, parsedHost} = this.props;

return (
<div className="section__title" onClick={handler}>
{title}
Expand All @@ -26,6 +29,12 @@ class BrowserTitle extends Component {
title="view in browser"
target="_blank">
</a>
<ClipboardButton
className="button section__icon section__icon_share"
onClick={(e) => e.stopPropagation()}
button-title="copy test link"
option-text={() => this._getTestUrl()}>
</ClipboardButton>
</div>
);
}
Expand All @@ -35,6 +44,19 @@ class BrowserTitle extends Component {
? url.format(Object.assign(url.parse(href), host))
: href;
}

_getTestUrl() {
const {browser, suite} = this.props;

return appendQuery(window.location.href, {
browser: browser.name,
testNameFilter: suite.suitePath.join(' '),
retryIndex: browser.state.retryIndex,
viewMode: 'all',
expand: 'all',
groupByError: false
});
}
}

export default connect(
Expand Down
2 changes: 1 addition & 1 deletion lib/static/components/section/title/simple.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class SectionTitle extends Component {
<ClipboardButton
onClick={(e) => e.stopPropagation()}
className="button section__icon section__icon_copy-to-clipboard"
title="copy to clipboard"
button-title="copy to clipboard"
data-clipboard-text={this.props.suite.suitePath.join(' ')}>
</ClipboardButton>
);
Expand Down
2 changes: 1 addition & 1 deletion lib/static/gui.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
}

.section__icon_retry:before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8 13A5 5 0 0 1 8 3a4.94 4.94 0 0 1 3.316 1.282L9 6.402l7.002 1.725L14.901 1l-2.097 1.92A6.958 6.958 0 0 0 8 1a7 7 0 1 0 0 14 6.995 6.995 0 0 0 6.256-3.872l-1.801-.901C11.633 11.865 9.957 13 8 13'/%3E%3C/svg%3E");
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath transform='scale(0.027,-0.027) translate(0,-448)' d=' M500.333 448H452.922C446.069 448 440.608 442.271 440.9360000000001 435.426L444.9020000000001 352.6670000000001C399.416 406.101 331.6720000000001 440 256.001 440C119.34 440 7.899 328.474 8 191.813C8.101 54.932 119.096 -56 256 -56C319.926 -56 378.202 -31.813 422.178 7.908C427.291 12.526 427.532 20.469 422.6600000000001 25.341L388.689 59.312C384.223 63.778 377.0490000000001 64.029 372.3090000000001 59.855C341.308 32.552 300.606 16 256 16C158.733 16 80 94.716 80 192C80 289.267 158.716 368 256 368C316.892 368 370.506 337.142 402.099 290.2L300.574 295.065C293.729 295.3930000000001 288 289.932 288 283.079V235.668C288 229.041 293.373 223.668 300 223.668H500.333C506.96 223.668 512.333 229.041 512.333 235.668V436C512.333 442.627 506.96 448 500.333 448z'/%3E%3C/svg%3E");
}

.section_status_success > .section__title:before,
Expand Down
27 changes: 27 additions & 0 deletions lib/static/modules/custom-queries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';

import {parseQuery} from './query-params';
import {pick, has} from 'lodash';

const allowedViewModes = new Set(['all', 'failed']);

export function getViewQuery(queryString) {
const query = parseQuery(queryString, {browser: 'filteredBrowsers'});

if (typeof query.filteredBrowsers === 'string') {
query.filteredBrowsers = [query.filteredBrowsers];
}

if (has(query, 'viewMode') && !allowedViewModes.has(query.viewMode)) {
query.viewMode = 'all';
}

return pick(query, [
'filteredBrowsers',
'testNameFilter',
'retryIndex',
'viewMode',
'expand',
'groupByError'
]);
}
54 changes: 54 additions & 0 deletions lib/static/modules/query-params.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';

import qs from 'qs';
import {assign} from 'lodash';

export function parseQuery(queryString, rename = {}) {
if (!queryString || typeof queryString !== 'string') {
return {};
}

if (queryString[0] === '?') {
queryString = queryString.slice(1);
}

return qs.parse(queryString, {
decoder: (str, defaultDecoder, charset, type) => {
if (type === 'key') {
const key = defaultDecoder(str, null, charset);
return rename[key] ? rename[key] : key;
}

const value = defaultDecoder(str, null, charset);

if (value === 'true') {
return true;
}

if (value === 'false') {
return false;
}

if (value && !isNaN(value)) {
return Number(value);
}

return value;
}}
);
}

export function appendQuery(url, query) {
const resultUrl = new URL(url);

const urlQuery = resultUrl.search || '';

const resultQuery = assign(
qs.parse(urlQuery.slice(1)),
query
);

resultUrl.search = qs.stringify(resultQuery, {arrayFormat: 'repeat'});

return resultUrl.href;
}
84 changes: 26 additions & 58 deletions lib/static/modules/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
} from './database-utils';
import {groupErrors} from './group-errors';
import * as localStorageWrapper from './local-storage-wrapper';
import {getViewQuery} from './custom-queries';
import testStatus from '../../constants/test-statuses';
import {isSqlite} from '../../common-utils';

Expand All @@ -29,27 +30,33 @@ const compiledData = window.data || defaultState;
function getInitialState(data) {
const {
skips, suites, config, total, updated, passed,
failed, skipped, warned, retries, perBrowser, apiValues, gui = false, date, saveFormat
failed, skipped, warned, retries, perBrowser, apiValues, gui = false, autoRun, date, saveFormat
} = data;
const {errorPatterns, scaleImages, lazyLoadOffset, defaultView: viewMode} = config;
const parsedURL = new URL(window.location.href);
const filteredBrowsers = parsedURL.searchParams.getAll('browser');
const viewQuery = getViewQuery(window.location.search);

let formattedSuites;
if (isSqlite(saveFormat)) {
formattedSuites = {};
} else {
formattedSuites = formatSuitesData(filterSuites(suites, filteredBrowsers));
formattedSuites = formatSuitesData(filterSuites(suites, viewQuery.filteredBrowsers));
}

const groupedErrors = groupErrors({suites: formattedSuites.suites, viewMode, errorPatterns, filteredBrowsers});
const groupedErrors = groupErrors(
assign({
suites: formattedSuites.suites,
viewMode,
errorPatterns
}, viewQuery)
);

localStorageWrapper.updateDeprecatedKeys();
const view = localStorageWrapper.getItem('view', {});
const host = _loadBaseHost(config.baseHost, localStorageWrapper);
const host = _loadBaseHost(compiledData.config.baseHost, localStorageWrapper);

return merge(defaultState, {
return merge({}, defaultState, {
gui,
autoRun,
skips,
groupedErrors,
config,
Expand All @@ -63,55 +70,16 @@ function getInitialState(data) {
view: merge({
viewMode,
scaleImages,
lazyLoadOffset,
filteredBrowsers
}, host, view)
lazyLoadOffset
}, host, view, viewQuery)
}, formattedSuites);
}

export default withBrowserStorage(reducer);
function reducer(state = getInitialState(compiledData), action) {
switch (action.type) {
case actionNames.VIEW_INITIAL: {
const {
gui,
autoRun,
suites,
skips,
apiValues,
saveFormat,
config
} = action.payload;
const {scaleImages, lazyLoadOffset, errorPatterns} = config;
const {filteredBrowsers, testNameFilter, viewMode} = state.view;

const formattedSuites = formatSuitesData(filterSuites(suites, filteredBrowsers));
const groupedErrors = groupErrors({
suites: formattedSuites.suites,
viewMode,
errorPatterns,
filteredBrowsers,
testNameFilter
});

const view = localStorageWrapper.getItem('view', {});

return merge(
{},
state,
{
config,
gui,
autoRun,
skips,
groupedErrors,
apiValues,
saveFormat,
view: {scaleImages, lazyLoadOffset}
},
formattedSuites,
{view}
);
return getInitialState(action.payload);
}
case actionNames.RUN_ALL_TESTS: {
const suites = clone(state.suites);
Expand Down Expand Up @@ -294,14 +262,14 @@ function createTestResultsFromDb(state, action) {
failed: getFailedSuiteIds(suites).sort()
};

const parsedURL = new URL(window.location.href);
const filteredBrowsers = parsedURL.searchParams.getAll('browser');
const groupedErrors = groupErrors({
suites,
viewMode: state.view.viewMode,
errorPatterns: state.config.errorPatterns,
filteredBrowsers
});
const viewQuery = getViewQuery(window.location.search);
const groupedErrors = groupErrors(
assign({
suites,
viewMode: state.view.viewMode,
errorPatterns: state.config.errorPatterns
}, viewQuery)
);

return {
...state,
Expand Down Expand Up @@ -418,7 +386,7 @@ function addTestResult(state, action) {

test.browsers.forEach((b) => {
if (b.name === browserId) {
Object.assign(b, browserResult);
assign(b, browserResult);
}
});
setStatusForBranch(suites, suitePath);
Expand Down
8 changes: 6 additions & 2 deletions lib/static/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,15 @@
}

.section__icon_view-local:before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M0 8.003c0-1.8 2.857-6 8-6s8 4.2 8 6c0 1.8-2.857 6-8 6s-8-4.2-8-6zm2 0c0 .362.546 1.377 1.44 2.217 1.193 1.12 2.75 1.783 4.56 1.783 1.81 0 3.367-.663 4.56-1.783.894-.84 1.44-1.855 1.44-2.217 0-.362-.546-1.377-1.44-2.218C11.366 4.665 9.81 4.003 8 4.003c-1.81 0-3.367.663-4.56 1.782C2.547 6.625 2 7.641 2 8.003zm8.3 0a2.3 2.3 0 1 1-4.6 0 2.3 2.3 0 0 1 4.6 0z'/%3E%3C/svg%3E");
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath transform='scale(0.027,-0.027) translate(0,-448)' d=' M569.354 216.369C512.969 312.051 407.81 376 288 376C168.14 376 63.004 312.006 6.646 216.369A47.999 47.999 0 0 1 6.646 167.63C63.031 71.949 168.19 8 288 8C407.86 8 512.996 71.994 569.354 167.631A47.997 47.997 0 0 1 569.354 216.369zM288 56C212.838 56 152 116.827 152 192C152 267.1620000000001 212.826 328 288 328C363.1620000000001 328 424 267.174 424 192C424 116.838 363.174 56 288 56zM392 192C392 134.562 345.438 88 288 88S184 134.562 184 192C184 209.708 188.431 226.379 196.236 240.973L196.235 240.941C196.235 217.29 215.408 198.118 239.059 198.118S281.883 217.291 281.883 240.941C281.883 264.592 262.71 283.765 239.059 283.765L239.027 283.764C253.621 291.569 270.292 296 288 296C345.438 296 392 249.438 392 192z'/%3E%3C/svg%3E");
}

.section__icon_copy-to-clipboard:before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M4 2.003a1 1 0 0 0 1 1h7.5a.5.5 0 0 1 .5.5V11a1 1 0 1 0 2 0V3.503a2.5 2.5 0 0 0-2.5-2.5H5a1 1 0 0 0-1 1zm-1.5 2h8a1.5 1.5 0 0 1 1.5 1.5v8a1.5 1.5 0 0 1-1.5 1.5h-8a1.5 1.5 0 0 1-1.5-1.5v-8a1.5 1.5 0 0 1 1.5-1.5zm.5 2v7h7v-7H3z'/%3E%3C/svg%3E");
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath transform='scale(0.027,-0.027) translate(0,-448)' d=' M464 448H144C117.49 448 96 426.51 96 400V352H48C21.49 352 0 330.51 0 304V-16C0 -42.51 21.49 -64 48 -64H368C394.51 -64 416 -42.51 416 -16V32H464C490.51 32 512 53.49 512 80V400C512 426.51 490.51 448 464 448zM362 -16H54A6 6 0 0 0 48 -10V298A6 6 0 0 0 54 304H96V80C96 53.49 117.49 32 144 32H368V-10A6 6 0 0 0 362 -16zM458 80H150A6 6 0 0 0 144 86V394A6 6 0 0 0 150 400H458A6 6 0 0 0 464 394V86A6 6 0 0 0 458 80z'/%3E%3C/svg%3E");
}

.section__icon_share:before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath transform='scale(0.027,-0.027) translate(0,-448)' d=' M561.938 289.94L417.94 433.908C387.926 463.922 336 442.903 336 399.968V342.77C293.55 340.89 251.97 336.2200000000001 215.24 324.7800000000001C180.07 313.8300000000001 152.17 297.2000000000001 132.33 275.36C108.22 248.8 96 215.4 96 176.06C96 114.363 129.178 63.605 180.87 31.3C218.416 7.792 266.118 43.951 251.89 87.04C236.375 134.159 234.734 157.963 336 165.8V112C336 69.007 387.968 48.087 417.94 78.06L561.938 222.06C580.688 240.8 580.688 271.2 561.938 289.94zM384 112V215.84C255.309 213.918 166.492 192.65 206.31 72C176.79 90.45 144 123.92 144 176.06C144 285.394 273.14 295.007 384 295.91V400L528 256L384 112zM408.74 27.507A82.658 82.658 0 0 1 429.714 36.81C437.69 41.762 448 35.984 448 26.596V-16C448 -42.51 426.51 -64 400 -64H48C21.49 -64 0 -42.51 0 -16V336C0 362.51 21.49 384 48 384H180C186.627 384 192 378.627 192 372V367.514C192 362.597 189.013 358.145 184.431 356.362C170.729 351.031 158.035 344.825 146.381 337.777A12.138 12.138 0 0 0 140.101 336H54A6 6 0 0 1 48 330V-10A6 6 0 0 1 54 -16H394A6 6 0 0 1 400 -10V15.966C400 21.336 403.579 26.025 408.74 27.507z' /%3E%3C/svg%3E");
}

.section_collapsed .section__title:before,
Expand Down
Loading

0 comments on commit 1d83322

Please sign in to comment.