-
Notifications
You must be signed in to change notification settings - Fork 63
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 lastFailed config option #973
Conversation
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.
Otherwise, /ok
But let's see what the others say
docs/config.md
Outdated
@@ -593,6 +594,9 @@ export const { | |||
} | |||
``` | |||
|
|||
### failedTestsPath | |||
Testplane will save a list of all failed tests to `failedTestsPath` |
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.
Would be nice to specify the default value here.
src/cli/index.js
Outdated
@@ -58,6 +58,7 @@ exports.run = (opts = {}) => { | |||
"--update-refs", | |||
'update screenshot references or gather them if they do not exist ("assertView" command)', | |||
) | |||
.option("--run-failed", "only run tests that failed during the last run") |
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.
I think it may be named something like --run-last-failed
, that would sound more clear
src/cli/index.js
Outdated
@@ -78,6 +79,7 @@ exports.run = (opts = {}) => { | |||
set: sets, | |||
grep, | |||
updateRefs, | |||
runFailed, |
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.
Also, perhaps there should be an option in config and env variable for that as well? I'm not sure though. Thinking about using this in CI, it would be convenient to have separate config for CI to run only failed tests by default or specify it by using env variable. Discussble
src/testplane.ts
Outdated
} | ||
|
||
protected async _saveFailed(): Promise<void> { | ||
const dirname = path.dirname(this._config.failedTestsPath); |
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.
Thinking about using this in CI, I think it would be convenient to be able to fetch this file from remote URL...
But that'd require to have two separate options for input and output. Generally, let's discuss with the team if we need run options like grep and files to be in config?
This would make possible doing so:
export default {
grep: /some-test/,
lastFailed: {
only: true,
output: '.testplane/file.json',
input: 'https://ci-artifacts.com/.../file.json',
}, // or just true/false if default input/output are sufficient
// other config options
}
src/testplane.ts
Outdated
}); | ||
|
||
this.on(MasterEvents.AFTER_TESTS_READ, testCollection => { | ||
if (previousRunFailedTests.length) { |
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.
I'm not sure if this is the best way to do this. In src/test-reader/test-parser
we have this code:
if (grep) {
treeBuilder.addTestFilter(test => grep.test(test.fullTitle()));
}
We probably can implement this the same way, by adding a filter function.
The goal is to keep main file src/testplane.ts
short and clean.
src/testplane.ts
Outdated
previousRunFailedTests.forEach(({ fullTitle, browserId }) => { | ||
testCollection.enableTest(fullTitle, browserId); | ||
}); | ||
} |
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.
Also, if run-failed option was passed, but no failed tests file was found, should we print a warning about it, what do you think?
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 are some questions
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.
Everything is cool, but I suggest using Map for matching and add browserVersion
to failedTests
, because we can use few versions of one browser.
src/test-reader/test-parser.js
Outdated
this.#failedTests = await fs.readJSON(config.lastFailed.input); | ||
} catch { | ||
logger.warn( | ||
`Could not read failed tests data at ${this._config.lastFailed.input}. Running all tests instead`, |
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.
To my mind we should write an error here, because of which we couldn't read the file.
src/test-reader/test-parser.js
Outdated
@@ -66,6 +70,16 @@ class TestParser extends EventEmitter { | |||
const esmDecorator = f => f + `?rand=${rand}`; | |||
await readFiles(files, { esmDecorator, config: mochaOpts, eventBus }); | |||
|
|||
if (config.lastFailed.only) { | |||
try { | |||
this.#failedTests = await fs.readJSON(config.lastFailed.input); |
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.
Can we store tests like object with uniq id as string? It gives ability to match tests by key faster then iterate by array.
Moreover we should save browserVersion
because we have pluign like https://github.com/gemini-testing/hermione-browser-version-changer/tree/master which gives ability to run tests in different browser versions. More info here - https://nda.ya.ru/t/ctpevAUg777hZX.
So to my mind we can use Map
like this:
#failedTests: Map<string, {fullTitle: string, browserId: string, browserVersion?: string}>;
// ...
this.#failedTests = new Map();
for (test of await fs.readJSON(config.lastFailed.input)) {
const id = getShortMD5(`${test.fullTitle}${test.browserId}${test.browserVersion}`);
this.#failedTests.set(id, _.pick(test, ['fullTitle', 'browserId', 'browserVersion']);
});
src/test-reader/test-parser.js
Outdated
const failedStringified = this.#failedTests.map(data => JSON.stringify(data)); | ||
treeBuilder.addTestFilter(test => | ||
failedStringified.includes( | ||
JSON.stringify({ |
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.
I don't like solution with iterate through all tests in order to find desired one. I described the above solution with Map
, which should be much faster. What do you think?
It will also allow us to get rid of json.strigify
here.
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.
The source is ok, but I don't see a lot of tests on this new functionality. Let's add them.
src/test-reader/test-parser.js
Outdated
: config.lastFailed.input.split(",").map(v => v.trim()); | ||
for (const inputPath of inputPaths) { | ||
for (const test of await fs.readJSON(inputPath)) { | ||
this.#failedTests.add(getShortMD5(`${test.fullTitle}${test.browserId}${test.browserVersion}`)); |
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.
Let's maybe put it in a separate function? Like this:
const getFailedTestId = (test) => getShortMD5(`${test.fullTitle}${test.browserId}${test.browserVersion}`);
test/src/config/options.js
Outdated
|
||
Config.read.returns(readConfig); | ||
|
||
assert.throws(() => createConfig(), Error, '"lastFailed.input" must be a string'); |
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.
Error message was changed
}); | ||
}); | ||
|
||
describe("input", () => { |
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.
I don't see tests:
- string without
.json
at the end -> throw - array without string +
.json
at the end -> throw - string with
.json
-> ok - array with
.json
-> ok
|
||
Config.read.returns(readConfig); | ||
|
||
assert.throws(() => createConfig(), Error, '"lastFailed.output" must be a string'); |
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.
Error message was changed
}); | ||
}); | ||
|
||
describe("output", () => { |
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.
I don't see tests:
- string without .json at the end -> throw
- string with .json -> ok
@@ -14,6 +14,7 @@ const proxyquire = require("proxyquire").noCallThru(); | |||
const path = require("path"); |
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.
Where are the tests for this module? There should be quite a lot of them here since the main code is in this file.
In loadFiles
(public) we can check that:
config.lastFailed.only
not set -> do not read json filesconfig.lastFailed.only
not set -> do not call addTestFilter (parse
should be called afterloadFiles
)config.lastFailed.only
set -> read json from one file (string)config.lastFailed.only
set -> read json from one file (string with commas, case with env)config.lastFailed.only
set -> read json from few files (array of strings)- and other tests
Method parse
must be tested as well.
test/src/testplane.js
Outdated
|
||
return runTestplane().then(success => assert.isFalse(success)); | ||
}); | ||
|
||
it("should halt if there were some errors", () => { |
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.
Why you remove this test?
@@ -467,16 +497,20 @@ describe("testplane", () => { | |||
it("all runner events with passed event data", () => { | |||
const runner = mkNodejsEnvRunner_(); | |||
const testplane = mkTestplane_(); | |||
const results = { | |||
fullTitle: () => "Title", | |||
browserId: "chrome", |
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.
then maybe add browserVersion
as well?
@@ -761,8 +795,13 @@ describe("testplane", () => { | |||
it('should return "true" after some test fail', () => { | |||
const testplane = mkTestplane_(); | |||
|
|||
const results = { | |||
fullTitle: () => "Title", | |||
browserId: "chrome", |
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.
then maybe add browserVersion
as well?
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.
I suggest fix few tests, but otherwise everything is ok!
src/test-reader/test-parser.js
Outdated
this.#buildInstructions = new InstructionsList(); | ||
} | ||
|
||
getFailedTestId = test => getShortMD5(`${test.fullTitle}${test.browserId}${test.browserVersion}`); |
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.
It should be a helper, because it doesn't use class methods
test/src/test-reader/test-parser.js
Outdated
|
||
await parse_({ config }, config); | ||
|
||
assert.calledOnce(TreeBuilder.prototype.addTestFilter); |
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.
We can get callback using - TreeBuilder.prototype.addTestFilter.lastCall.args[0]
and call it with passed test. And then check if it returns true
or false
. You can see examples of testing grep
in this file.
deee61f
to
b85ef02
Compare
Make it possible to run only failed tests by adding --run-failed option in CLI.