Skip to content

Commit

Permalink
Merge pull request #457 from gemini-testing/HERMIONE-138.steps_reporter
Browse files Browse the repository at this point in the history
fix: add steps support
  • Loading branch information
KuznetsovRoman authored Jan 30, 2023
2 parents ac9791a + b498f47 commit 7019a4d
Show file tree
Hide file tree
Showing 17 changed files with 219 additions and 78 deletions.
1 change: 1 addition & 0 deletions lib/constants/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const SUITES_TABLE_COLUMNS = [
{name: 'name', type: DB_TYPES.text},
{name: 'suiteUrl', type: DB_TYPES.text},
{name: 'metaInfo', type: DB_TYPES.text},
{name: 'history', type: DB_TYPES.text},
{name: 'description', type: DB_TYPES.text},
{name: 'error', type: DB_TYPES.text},
{name: 'skipReason', type: DB_TYPES.text},
Expand Down
46 changes: 32 additions & 14 deletions lib/history-utils.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,50 @@
'use strict';

const {last, isUndefined} = require('lodash');
const {isEmpty} = require('lodash');

const formatDuration = (d) => `<- ${d}ms`;

const wrapArg = (arg) => `"${arg}"`;

const getCommand = ({
n: name,
a: args = [],
d: duration}
) => `${name}(${args.map(wrapArg).join(', ')}) ${formatDuration(duration)}`;
a: args = []
}) => `${name}(${args.map(wrapArg).join(', ')})`;

const traverseNodes = (nodes, traverseCb, depth = 0) => {
nodes.forEach(node => {
const shouldTraverseChildren = traverseCb(node, depth);

if (shouldTraverseChildren) {
traverseNodes(node.c, traverseCb, depth + 1);
}
});
};

const getCommandsHistory = (history) => {
if (isUndefined(history)) {
if (isEmpty(history)) {
return;
}

try {
const formatedCommands = history
.map(getCommand)
.map((s) => `\t${s}\n`);
const lastCommandChildren = last(history).c;
const formatedChildren = lastCommandChildren
.map((cmd) => `${getCommand(cmd)}`)
.map((s) => `\t\t${s}\n`);

return [...formatedCommands, ...formatedChildren];
const formatedHistory = [];

const traverseCb = (node, depth) => {
const offset = '\t'.repeat(depth);
const isStep = node.n === 'runStep';
const duration = node.d;
const isFailed = !!node.f;
const title = isStep ? node.a[0] : getCommand(node);
const formatedDuration = formatDuration(duration);

formatedHistory.push(`${offset}${title} ${formatedDuration}\n`);

return isFailed;
};

traverseNodes(history, traverseCb);

return formatedHistory;
} catch (e) {
return `failed to get command history: ${e.message}`;
}
Expand Down
9 changes: 5 additions & 4 deletions lib/report-builder/static.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = class StaticReportBuilder {

return result instanceof TestAdapter
? result
: TestAdapter.create(result, this._hermione, this._pluginConfig, status);
: TestAdapter.create(result, this._hermione, status);
}

async saveStaticFiles() {
Expand Down Expand Up @@ -111,16 +111,17 @@ module.exports = class StaticReportBuilder {

_createTestResult(result, props) {
const {
browserId, suite, sessionId, description, imagesInfo, screenshot, multipleTabs, errorDetails
browserId, suite, sessionId, description, history,
imagesInfo, screenshot, multipleTabs, errorDetails
} = result;

const {baseHost, saveErrorDetails} = this._pluginConfig;
const suiteUrl = suite.getUrl({browserId, baseHost});
const metaInfo = _.merge(_.cloneDeep(result.meta), {url: suite.fullUrl, file: suite.file, sessionId});

const testResult = Object.assign({
suiteUrl, name: browserId, metaInfo, description, imagesInfo,
screenshot: Boolean(screenshot), multipleTabs
suiteUrl, name: browserId, metaInfo, description, history,
imagesInfo, screenshot: Boolean(screenshot), multipleTabs
}, props);

if (saveErrorDetails && errorDetails) {
Expand Down
2 changes: 2 additions & 0 deletions lib/sqlite-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ module.exports = class SqliteAdapter {
name,
suiteUrl,
metaInfo,
history,
description,
error,
skipReason,
Expand All @@ -82,6 +83,7 @@ module.exports = class SqliteAdapter {
name,
suiteUrl,
metaInfo,
history,
description,
error,
skipReason,
Expand Down
29 changes: 29 additions & 0 deletions lib/static/components/section/body/history/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {isEmpty} from 'lodash';
import Details from '../../../details';

import './index.styl';

const History = ({history}) => (
isEmpty(history)
? null
: <Details
title='History'
content={history}
extendClassNames='details_type_text history'
/>
);

History.propTypes = {
resultId: PropTypes.string.isRequired,
// from store
history: PropTypes.arrayOf(PropTypes.string).isRequired
};

export default connect(({tree}, {resultId}) => {
const {history = []} = tree.results.byId[resultId];

return {history};
})(History);
4 changes: 4 additions & 0 deletions lib/static/components/section/body/history/index.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.history {
white-space: pre-wrap;
word-wrap: break-word;
}
2 changes: 2 additions & 0 deletions lib/static/components/section/body/result.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {Component, Fragment} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import MetaInfo from './meta-info';
import History from './history';
import Description from './description';
import Tabs from './tabs';
import ExtensionPoint from '../../extension-point';
Expand All @@ -26,6 +27,7 @@ class Result extends Component {
<Fragment>
<ExtensionPoint name={RESULT_META} result={result} testName={testName}>
<MetaInfo resultId={resultId} />
<History resultId={resultId} />
</ExtensionPoint>
{result.description && <Description content={result.description} />}
<Tabs result={result} />
Expand Down
21 changes: 8 additions & 13 deletions lib/test-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ const globalCacheDiffImages = new Map();
const testsAttempts = new Map();

module.exports = class TestAdapter {
static create(testResult = {}, hermione, pluginConfig, status) {
return new this(testResult, hermione, pluginConfig, status);
static create(testResult = {}, hermione, status) {
return new this(testResult, hermione, status);
}

constructor(testResult, hermione, pluginConfig, status) {
constructor(testResult, hermione, status) {
this._testResult = testResult;
this._hermione = hermione;
this._pluginConfig = pluginConfig;
this._errors = this._hermione.errors;
this._suite = SuiteAdapter.create(this._testResult);
this._imagesSaver = this._hermione.htmlReporter.imagesSaver;
Expand Down Expand Up @@ -220,16 +219,12 @@ module.exports = class TestAdapter {
return this.imagesInfo;
}

get error() {
const err = _.pick(this._testResult.err, ['message', 'stack', 'stateName']);
const {history} = this._testResult;
const {commandsWithShortHistory} = this._pluginConfig;

if (!_.isEmpty(history)) {
err.history = getCommandsHistory(history, commandsWithShortHistory);
}
get history() {
return getCommandsHistory(this._testResult.history);
}

return err;
get error() {
return _.pick(this._testResult.err, ['message', 'stack', 'stateName']);
}

get imageDir() {
Expand Down
1 change: 1 addition & 0 deletions lib/tests-tree-builder/static.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ function mkTestResult(row, data = {}) {
description: row[DB_COLUMN_INDEXES.description],
imagesInfo: JSON.parse(row[DB_COLUMN_INDEXES.imagesInfo]),
metaInfo: JSON.parse(row[DB_COLUMN_INDEXES.metaInfo]),
history: JSON.parse(row[DB_COLUMN_INDEXES.history]),
multipleTabs: Boolean(row[DB_COLUMN_INDEXES.multipleTabs]),
name: row[DB_COLUMN_INDEXES.name],
screenshot: Boolean(row[DB_COLUMN_INDEXES.screenshot]),
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 29 additions & 14 deletions test/unit/lib/history-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('history-utils', () => {

afterEach(() => sandbox.restore());

it('should return commands executed in test file and all sub commands of the last command', async () => {
it('should return commands executed in test file', () => {
const allHistory = [
{n: 'foo', a: ['foo-arg'], d: 10, c: []},
{n: 'baz', a: ['baz-arg'], d: 1, c: []},
Expand All @@ -23,28 +23,43 @@ describe('history-utils', () => {
]}
];

const history = await getCommandsHistory(allHistory);
const history = getCommandsHistory(allHistory);

assert.deepEqual(history, [
'\tfoo("foo-arg") <- 10ms\n',
'\tbaz("baz-arg") <- 1ms\n',
'\tbar("bar-arg") <- 3ms\n',
'\tqux("qux-arg") <- 4ms\n',
'\t\tqux("qux-arg") <- 4ms\n',
'\t\tbaz("bar-arg") <- 3ms\n'
'foo("foo-arg") <- 10ms\n',
'baz("baz-arg") <- 1ms\n',
'bar("bar-arg") <- 3ms\n',
'qux("qux-arg") <- 4ms\n'
]);
});

it('should return undefined if all history is not given', async () => {
const history = await getCommandsHistory(undefined);
it('should return commands executed in test file and all sub commands of the failed command', () => {
const allHistory = [
{n: 'foo', a: ['foo-arg'], d: 10, c: []},
{n: 'baz', a: ['baz-arg'], d: 1, c: []},
{n: 'bar', a: ['bar-arg'], d: 3, c: []},
{n: 'qux', a: ['qux-arg'], d: 4, f: true, c: [
{n: 'qux', a: ['qux-arg'], d: 4, c: []},
{n: 'baz', a: ['bar-arg'], d: 3, f: true, c: []}
]}
];

const history = getCommandsHistory(allHistory);

assert.isUndefined(history);
assert.deepEqual(history, [
'foo("foo-arg") <- 10ms\n',
'baz("baz-arg") <- 1ms\n',
'bar("bar-arg") <- 3ms\n',
'qux("qux-arg") <- 4ms\n',
'\tqux("qux-arg") <- 4ms\n',
'\tbaz("bar-arg") <- 3ms\n'
]);
});

it('should return failure message in case of exception', async () => {
const history = await getCommandsHistory([{}]);
it('should return undefined if all history is not given', () => {
const history = getCommandsHistory(undefined);

assert.match(history, /failed to get command history: .*/);
assert.isUndefined(history);
});
});
});
17 changes: 9 additions & 8 deletions test/unit/lib/sqlite-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,15 @@ describe('lib/sqlite-adapter', () => {
{cid: 2, name: 'name', type: 'TEXT'},
{cid: 3, name: 'suiteUrl', type: 'TEXT'},
{cid: 4, name: 'metaInfo', type: 'TEXT'},
{cid: 5, name: 'description', type: 'TEXT'},
{cid: 6, name: 'error', type: 'TEXT'},
{cid: 7, name: 'skipReason', type: 'TEXT'},
{cid: 8, name: 'imagesInfo', type: 'TEXT'},
{cid: 9, name: 'screenshot', type: 'INT'},
{cid: 10, name: 'multipleTabs', type: 'INT'},
{cid: 11, name: 'status', type: 'TEXT'},
{cid: 12, name: 'timestamp', type: 'INT'}
{cid: 5, name: 'history', type: 'TEXT'},
{cid: 6, name: 'description', type: 'TEXT'},
{cid: 7, name: 'error', type: 'TEXT'},
{cid: 8, name: 'skipReason', type: 'TEXT'},
{cid: 9, name: 'imagesInfo', type: 'TEXT'},
{cid: 10, name: 'screenshot', type: 'INT'},
{cid: 11, name: 'multipleTabs', type: 'INT'},
{cid: 12, name: 'status', type: 'TEXT'},
{cid: 13, name: 'timestamp', type: 'INT'}
];

const columns = db.prepare('PRAGMA table_info(suites);').all();
Expand Down
51 changes: 51 additions & 0 deletions test/unit/lib/static/components/section/body/history.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import {defaultsDeep, set} from 'lodash';
import proxyquire from 'proxyquire';
import {mkConnectedComponent} from '../../utils';

describe('<History />', () => {
const sandbox = sinon.sandbox.create();
let History, Details;

beforeEach(() => {
Details = sinon.stub().returns(null);

History = proxyquire('lib/static/components/section/body/history', {
'../../../details': {default: Details}
}).default;
});

afterEach(() => sandbox.restore());

const mkHistoryComponent = (props = {}, initialState = {}) => {
props = defaultsDeep(props, {
resultId: 'default-result'
});

return mkConnectedComponent(<History {...props} />, {initialState});
};

it('should not render if history does not exists', () => {
const initialState = set({}, 'tree.results.byId.default-result', {});

mkHistoryComponent({resultId: 'default-result'}, initialState);

assert.notCalled(Details);
});

it('should render history if exists', () => {
const initialState = set({}, 'tree.results.byId.default-result.history', 'some-history');

const component = mkHistoryComponent({resultId: 'default-result'}, initialState);

assert.equal(component.find(Details).prop('content'), 'some-history');
});

it('should render with "History" title', () => {
const initialState = set({}, 'tree.results.byId.default-result.history', 'some-history');

const component = mkHistoryComponent({resultId: 'default-result'}, initialState);

assert.equal(component.find(Details).prop('title'), 'History');
});
});
Loading

0 comments on commit 7019a4d

Please sign in to comment.