Closed danDanV1 closed 5 years ago
Yes. This is an important use case! I see you explicitly add a step
-- which is of course completely reasonable.
That said, I would like to propose that this should work automatically...
for (let index = 0; index < images.length; index++) {
backstopScrollIntoView(images[index], true);
await assertImageLoaded(images[index], assert);
await backstop(assert);
}
and say, produce output like this...
ember-backstoptest_Integration__Component__my_component_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Step_1_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Step_2_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Step_3_0_document_0_webview.png
Where the first assert is identical to current behavior and subsequent asserts are auto-incremented. This is how backstop handles selector expansion and also this counting and labeling would be more ergonomic if abstracted away.
I think it's totally reasonable to also append an assert name if supplied. This would allow your use-case where you provide this context to the end user. e.g.
await backstop(assert, {name: 'this gets appended after step ID'});
I imagine the assert object (which I believe lives for the duration of the test) could be used to track backstop() calls. Maybe this is a cleaner experience? Any thoughts?
I agree. Sounds like a cleaner API to abstract away the steps. I had the steps in there manually because I didn't know how to keep state between backstop assertions.
In that case, perhaps we could use the built-in assert.step()
method, which can be used with verifySteps()
or do we attach our own array to assert so we don't pollute the steps functionality. eg. track it via assert.backstop
ATM I was sketching out a solution, I started with asset.step() / assert.steps[] which is built in. Now I'm leaning towards assert.backstop[]
for keeping track of the state.
Nice. Yes -- seems like assert.backstop[]
is good. Or potentially assert.backstop.steps[]
is also good and reasonably future-proof. Either way -- Great!
I think I've got something that will cover it.
Before I commit the pull, there might be something I can fix too. I don't know anything about Mocha tests, but I think there might be a bug around line 90 'else if' of backstop.js helper. testHash
will be sent as null in the payload. Is there another way the backstop server will acquire the testid/scenarioid? Should setting testHash
be place above the entire if statement?
// Automatic name generation for QUnit tests by passing in the `assert` object.
if (name.test && name.test.module && name.test.module.name && name.test.testName) {
testHash.testId = window._testRunTime;
testHash.scenarioId = name.test.testId;
name = `${name.test.module.name} | ${name.test.testName}`;
} else if (name.fullTitle) {
// Automatic name generation for Mocha tests by passing in the `this.test` object.
name = name.fullTitle();
//!QUESTION!! doesn't testHash need to be set here too?
}
Because later this happens:
const payload = JSON.stringify({
content,
name: name,
widths: options.widths,
breakpoints: options.breakpoints,
enableJavaScript: options.enableJavaScript,
testHash: testHash,
origin: ORIGIN,
scenario: options.scenario
});
or perhaps Mocha doesn't have test.ids?
Thanks for the attention here -- much appreciated. So I don't think we actually need the else if
at all. We can just delete it since we are not specifically supporting Mocha here.
And also, we should really rename that name
variable to assert
. Calling that variable name
is very misleading. (I think originally this was to support passing just a string. Now I don't think we support that passing a string there.)
ha, yes, I renamed it long ago in my own backstop helper. it was confusing.
so this is what I came up with.
Automatic steps
for (let index = 0; index < images.length; index++) {
await backstop(assert);
}
Matches the output you requested earlier:
ember-backstoptest_Integration__Component__my_component_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Step_1_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Step_2_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Step_3_0_document_0_webview.png
Append a custom name to the end of the step/assertion
for (let index = 0; index < images.length; index++) {
await backstop(assert, { name: `Image ${index} rendered` });
}
ember-backstoptest_Integration__Component__my_component_0_document__image_0_rendered_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Step_1__image_1_rendered_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Step_2__image_2_rendered_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Step_3__image_3_rendered_0_document_0_webview.png
Append a custom name to the end of assertion
await backstop(assert, { name: `did something cool` });
ember-backstoptest_Integration__Component__my_component_0_document__did_something_cool_0_document_0_webview.png
Code changes -more or less.
function createNameHash(assert, options) {
let name = options && options.name ? options.name : ""; //optional name to append to the assertion
let assertionName;
let testHash = {};
//namespace our steps to not conflict with qunit steps functionality
if (!assert.test.backstop) assert.test.backstop = {};
if (!assert.test.backstop.steps) assert.test.backstop.steps = [];
let steps = assert.test.backstop.steps;
if (name && name.length !== 0) {
name = ` | ${name}`;
}
// Generate base names to extend
if (
assert.test &&
assert.test.module &&
assert.test.module.name &&
assert.test.testName
) {
testHash.testId = window._testRunTime;
testHash.scenarioId = assert.test.testId;
assertionName = `${assert.test.module.name} | ${assert.test.testName}`;
}
//Record our step
steps.push(steps.length);
// Do not include a step count in the first assertion
if (steps.length === 1) {
return { name: `${assertionName}${name}`, testHash: testHash };
}
// Name generation based on steps and optional name for the step
const stepNumber = steps.length - 1;
assertionName = `${assertionName} | Step#${stepNumber}${name}`;
return { name: assertionName, testHash: testHash };
}
/**
* I'm in your webapps -- checkin your screenz. -schooch
*/
export default async function(assert, options) {
const hash = createNameHash(assert, options);
return new Promise((res, err) => {
backstopHelper(hash.name, hash.testHash, options, res, err);
}).then(backstopResult => {
assert.ok(backstopResult.ok, backstopResult.message);
});
}
Awesome!
Questions...
[a-zA-Z0-9_-]
).Yes, I am using the refactored helper above in my test suite in all usage scenarios.
Agreed to change order.
I don't see an issue with regex. The label will have all sorts of characters from the qunit test title and we can't control user input, and it's nice to have a easily readable label when reviewing the backstop report. I will remove the #
but we should plan for the regex to sanitize.
This is how it currently renders in backstop report:
label: Integration | Component | web/item/image-single/image-only | visualRegression :: object-fit :: scroll-x | image is constrained by height | Step#1 | Image 1 rendered selector: document
filename: ember-backstoptest_Integration__Component__web_item_image-single_image-only__visualRegression__object-fit__scroll-x__image_is_constrained_by_height__Step1__Image_1_rendered_0_document_0_webview.png
But since we changing the order, and it's a bit odd to not have step0, then have step1 in the second assertion, I'd propose to rename step#
to assertion
and always include it.
Rendered from my test suite:
label: Integration | Component | web/item/image-single/image-only | visualRegression :: object-fit :: scroll-x | image is constrained by height | Image 1 rendered | Assertion 1 selector: document
filename: ember-backstoptest_Integration__Component__web_item_image-single_image-only__visualRegression__object-fit__scroll-x__image_is_constrained_by_height__Image_1_rendered__Assertion_1_0_document_0_webview.png
And just to match the examples we've been using
await backstop(assert);
ember-backstoptest_Integration__Component__my_component__Assertion_0_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Assertion_1_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__Assertion_2_0_document_0_webview.png
and with the custom name...
await backstop(assert, { name:
My cool title});
ember-backstoptest_Integration__Component__my_component__my_cool_title__Assertion_0_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__my_cool_title__Assertion_1_0_document_0_webview.png
ember-backstoptest_Integration__Component__my_component__my_cool_title__Assertion_2_0_document_0_webview.png
Question: should we count assertion# from 0 or 1?
Thanks @edeis53! That is all reasonable -- and good point on the unpredictable characters in the test names.
Responses...
assertion
and the assertion value. (Your call though)assertion
label to assert
or even a
(Again your call)Thanks for your help with this other cool feature!
@garris since you like my last enhancement...
I customized my backstop helper to allow steps and custom naming. Right now ember-backstop is limited to one visual assert per test without this change.
In our app, the components we are testing can change state multiple times in a single test, eg. image placeholder, preloading animation, and loaded state. Also in-viewport detection and we need to scroll, etc. So it's much easier to test multiple steps in a single test.
or even a more fun test
you can even just pass a custom string
In the above, I have the custom naming a second argument and the options as a third. With a little thinking it could be merged into just one options argument.
Right now my personal API for the helper is:
Looks a bit like this:
produces: