Skip to content
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: add grouping by error type #213

Merged
merged 1 commit into from
Apr 2, 2019
Merged

feat: add grouping by error type #213

merged 1 commit into from
Apr 2, 2019

Conversation

CatWithApple
Copy link
Contributor

@CatWithApple CatWithApple commented Mar 25, 2019

Аdded a button "Group by error" that groups tests by error messages. Patterns for grouping can be specified in the configuration.
I will write more unit tests after approving main code.

@CatWithApple CatWithApple marked this pull request as ready for review March 26, 2019 11:28
@CatWithApple CatWithApple requested review from DudaGod, sipayRT and rostik404 and removed request for DudaGod March 26, 2019 11:29
README.md Outdated
@@ -24,6 +24,10 @@ directory.
* **baseHost** (optional) - `String` - it changes original host for view in the browser; by default original host does not change
* **scaleImages** (optional) – `Boolean` – fit images into page width; `false` by default
* **lazyLoadOffset** (optional) - `Number` - allows you to specify how far above and below the viewport you want to begin loading images. Lazy loading would be disabled if you specify 0. `800` by default.
* **errorPatterns** (optional) - `Array` - error message patterns for 'Group by error' mode.
Array element must be `Object` ({'*name*': `String`, '*pattern*': `String`}) or `String` (interpret as *name* and *pattern*).
Test will be associated with group if `errorTest.message.match(new RegExp(pattern))`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Давай как-то так - Test will be associated with group if test error matches on group error pattern, чтобы в доку куски кода не вставлять.


constructor(props) {
super(props);
this._toggleState = this._toggleState.bind(this);
Copy link
Collaborator

Choose a reason for hiding this comment

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

А чего не через стрелочную функцию, чтобы без биндинга?
https://medium.com/silesis/handle-events-in-react-with-arrow-functions-ede88184bbb


const body = this.state.collapsed
? null
: <div className="error_group__body error_group__body_guided">
Copy link
Collaborator

Choose a reason for hiding this comment

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

error-group__body_guided - го по БЭМ-у

<div className={className}>
<div className="error_group__title" onClick={this._toggleState} title={group.pattern}>
<div className="error_group__name">{group.name}</div>
<div className="error_group__count">&nbsp;({group.count})</div>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Кажется, здесь перебор с дивами. italic можно сделать с помощью тега <i>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

diveloper жи есть

export default connect(
(state) => ({
groupedErrors: state.groupedErrors
})
Copy link
Collaborator

Choose a reason for hiding this comment

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

го в 1 строчку

* @param {string} [filterByName]
* @return {array}
*/
function groupErrors(suites, errorPatterns, filteredBrowsers, filterByName = '') {
Copy link
Collaborator

Choose a reason for hiding this comment

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

filterByName - звучит, как название функции
Я бы nameFilterPattern назвал

group.tests[testName].push(browserName);
group.count++;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Давай этот for перепишем через reduce и в функцию вынесем. groupErrors занимает 52 строчки - ее точно рефакторить нужно.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

функцию вынес. не понял, чем поможет reduce читаемости

}
if (!group.tests[testName].includes(browserName)) {
group.tests[testName].push(browserName);
group.count++;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Этот каунтер точно нужен?
Нельзя потом взять length от Object.keys(group.tests)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Нужно брать количество упавших тестов по браузерам, чтобы числа были в соответствии со статистикой сверху

if (!group.tests.hasOwnProperty(testName)) {
group.tests[testName] = [];
}
if (!group.tests[testName].includes(browserName)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Если group.tests[testName] сделать set-ом, а не массивом, эта проверка уйдет

}
return {
pattern: errorText
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

в одну строчку

Copy link
Contributor Author

Choose a reason for hiding this comment

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

зачем?

Copy link
Member

Choose a reason for hiding this comment

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

+1 за одну строчку, в читаемости код не потеряет, но будет короче. В этом и плюс.

@CatWithApple CatWithApple force-pushed the group-by-error branch 2 times, most recently from a9f9734 to 6d136fe Compare March 28, 2019 14:53
@CatWithApple
Copy link
Contributor Author

fixed.
added tests.

Copy link
Member

@DudaGod DudaGod left a comment

Choose a reason for hiding this comment

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

section-common сейчас слишком умный. Он ничего не должен знать про группировку по ошибкам

@@ -24,6 +24,10 @@ directory.
* **baseHost** (optional) - `String` - it changes original host for view in the browser; by default original host does not change
* **scaleImages** (optional) – `Boolean` – fit images into page width; `false` by default
* **lazyLoadOffset** (optional) - `Number` - allows you to specify how far above and below the viewport you want to begin loading images. Lazy loading would be disabled if you specify 0. `800` by default.
* **errorPatterns** (optional) - `Array` - error message patterns for 'Group by error' mode.
Array element must be `Object` ({'*name*': `String`, '*pattern*': `String`}) or `String` (interpret as *name* and *pattern*).
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
Contributor Author

Choose a reason for hiding this comment

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

если значение строка - то она интерпретируется и как шаблон и как название группы.
никаких разделителей.
пример добавлю

lib/config.js Outdated
errorPatterns: option({
defaultValue: configDefaults.errorPatterns,
parseEnv: JSON.parse,
validate: assertArray('errorPatterns')
Copy link
Member

Choose a reason for hiding this comment

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

я бы еще проверил, что элементы массива являются объектами или строками

@@ -4,30 +4,30 @@ import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import _ from 'lodash';
import {debounce} from 'lodash';
Copy link
Member

Choose a reason for hiding this comment

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

👍

(dispatch) => ({actions: bindActionCreators(actions, dispatch)})
)(FilterByNameInput);
)(TestNameFilterInput);
Copy link
Member

Choose a reason for hiding this comment

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

это можно было бы отдельным коммитом сделать

this.state = {
collapsed: true
};
}
Copy link
Member

Choose a reason for hiding this comment

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

красивее будет вот так:

state = {
  collapsed: true
}

В этом случае конструктор определять не нужно

} else if (typeof patternInfo === 'object') {
return {
pattern: patternInfo.pattern,
name: patternInfo.name,
Copy link
Member

Choose a reason for hiding this comment

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

почему у тебя в итоге в зависимости от типа получаются объекты с разными полями? Давай сделаем, чтобы поля у них были одинаковые. Тебе же так в коде удобнее будет.

errorPatterns = normalizeErrorPatterns(errorPatterns);
const testWithErrors = {};

extractErrors(suites, testWithErrors);
Copy link
Member

Choose a reason for hiding this comment

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

такая конструкция выглядет очень странной.

Почему так не написать:

const errors = extractErrors(suites);

if (suite.children) {
extractErrors(suite.children, testWithErrors);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Можно же проще написать.

function extractErrors(suites) {
  const extract = (node) => {
    if (node.children) {
      return flatMap(node.children, extract);
    }

    if (node.browsers) {
      return flatMap(node.browsers, extract);
    }

    if (isSuccessStatus(node.result.status)) {
      return [];
    }

    const broResults = browser.retries.concat(browser.result);
    const errors = extractErrorsFromRetries(broResults);

    return {[node.name]: errors};
  });

  return flatMap(suites, extract);
}

}
if (retry.error && retry.error.message) {
errorsInRetry.add(retry.error.message);
}
Copy link
Member

Choose a reason for hiding this comment

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

[retry].concat(retry.imagesInfo).forEach(({error}) => {
  if (_.get(error, 'message')) {
    errorsInRetry.add(error.message);
  }
});

}
return {
pattern: errorText
};
Copy link
Member

Choose a reason for hiding this comment

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

+1 за одну строчку, в читаемости код не потеряет, но будет короче. В этом и плюс.

@CatWithApple CatWithApple force-pushed the group-by-error branch 3 times, most recently from e44d18d to 3e066d4 Compare April 1, 2019 13:08
@CatWithApple
Copy link
Contributor Author

fixed

Copy link
Member

@DudaGod DudaGod left a comment

Choose a reason for hiding this comment

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

в остальном /ok

name: 'Cannot read property of undefined',
pattern: 'Cannot read property .* of undefined'
}
]
Copy link
Member

Choose a reason for hiding this comment

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

👍

pattern: patternInfo
};
}
return patternInfo;
Copy link
Member

Choose a reason for hiding this comment

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

Я бы так написал:

return _.isString(pattern) ? {name: pattern, pattern} : pattern;

}
}
}
};
Copy link
Member

Choose a reason for hiding this comment

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

🔥

}),
errorPatterns: option({
defaultValue: configDefaults.errorPatterns,
parseEnv: JSON.parse,
Copy link
Member

Choose a reason for hiding this comment

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

а почему ты тут parseCli не указал?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ты думаешь, кто-нибудь захочет передать список шаблонов ошибок через cli?

actions: PropTypes.object.isRequired
}

constructor(props) {
super(props);

this.state = {
filterByName: this.props.filterByName
testNameFilter: this.props.testNameFilter
};
Copy link
Member

Choose a reason for hiding this comment

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

Чего не захотел от конструктора избавиться? Тебе запись такая не нравиться?

mkSuiteTree({
browsers: [
mkBrowserResult({
result: mkTestResult({
Copy link
Member

Choose a reason for hiding this comment

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

выглядит так, что нужен хелпер который строит дерево только по переданному testResult и testRetry. Без явного определения всего дерева

})
]
})
];
Copy link
Member

Choose a reason for hiding this comment

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

У тебя тут стрела смерти получилась ) Тебя такое количество данных не смущает? Может нужен еще один уровень абстракции?

Я бы наверно попробовал написать хелпер, который бы на вход принимал стейт, а по suitePath определял сколько над ним должно быть сьютов. За счет этого код самого теста сильно бы упростился

count: 1,
name: 'message stub',
pattern: 'message stub',

Copy link
Member

Choose a reason for hiding this comment

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

лишняя пустая строка

name: 'message stub',
pattern: 'message stub',
tests: {
'default-suite default-state': ['browserOne', 'browserTwo']
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

Choose a reason for hiding this comment

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

сложные тесты получилось. Возможно стоило юзать какие-то фикстуры или попробовать спрятать часть данных внутри дополнительных хелперов

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Договорились c @sipayRT, что оформлю отдельный PR по тестам

@CatWithApple CatWithApple merged commit b12903a into master Apr 2, 2019
@CatWithApple CatWithApple deleted the group-by-error branch April 2, 2019 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants