-
Notifications
You must be signed in to change notification settings - Fork 33
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
E2E: Improve support to interact with panel edit options #1272
base: main
Are you sure you want to change the base?
Conversation
Hello! 👋 This repository uses Auto for releasing packages using PR labels. ✨ This PR can be merged but will not trigger a new release. To trigger a new release add the |
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.
Nice! I think I prefer option 1 as option 2 would make us slightly more vulnerable to changes in the core Playwright api.
Question: Would it be better to replace input
and switch
with getByRole(role: 'input' | 'switch' | ..., { name: string }: Options)
? This would make our abstraction look closer to the playwright api?
const group = panelEditPage.getOptionsGroup('Clock panel')
await group.getByRole('switch', { name: 'enableClock' }).uncheck()
Yeah, but on the other hand 'input' is not a role. In the scenario where with the switch-bug you would still need to know if you are trying to fetch a |
@sunker I added some more code and tests. Lets have a sync about this before I continue further but it feels kind of OK to work with when writing tests. |
Nice! Great tests! I had a quick look at the helper methods in the panel options API and this is what you can do:
So basically we need to support textbox, textarea, combobox, radiogroup and switch. So I guess we'll have to choose between: group.getByRole(role: textbox | textarea | combobox | radiogroup | switch, options: { ... })
// or implementing each specifically like in this PR. e.g
group.getRadioButtonGroup(label: string) I don't have strong opinions here. The tricky bit will be to get them to work in >= 8.5.0. |
I'm happy with both as well. The only thing I feel is a bit negative with the But I'm happy to proceed with that approach as well. Maybe ask for some more input here? |
You mean because they only make up a subset of all roles that exists?
Sounds good! Maybe Jack? He's affected by the recent change in his traffic light panel. :) |
Yes, and we would need to have custom roles that match to our components. E.g. in the scenario with the recent change the switch got changed from a 'checkbox' to a 'switch' and in those cases we would not want the developer to need to update their tests. In case they have I understood your suggestion as we don't use the default roles but use our own that maps to our components rather than the html roles. Otherwise we don't resolve issues as the one with the switch. |
packages/plugin-e2e/src/models/components/PanelEditOptionsGroup.ts
Outdated
Show resolved
Hide resolved
d073a1d
to
ef666ca
Compare
actual = await select | ||
.locator('div[class*="-grafana-select-value-container"]') | ||
.locator('div[class*="-singleValue"]') | ||
.innerText(options); |
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.
Isn't there an e2e selector for this? If not, we should add one to Grafana.
One problem with this and the other new matchers is that that they don't have access to the test context. There's no way for them to check grafanaVersion and use the resolved selectors.
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.
But IDK - the Selector class has access to the context, so maybe it can be encapsulated there 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.
hmm, that is a valid concern. But I think we can fix that in the index.ts
file.
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 ctx
is available with that the selectors as well. So I will see if I can change these to selectors from the grafana-selectors package istead.
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.
How? This is fine in older versions of Grafana, but we need a stable e2e-selector in Grafana to be future proof. Looks like there's a data-testid set here. Can we use that instead of the classnames?
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.
hmm, I don't think we pass the data-testid when rendering the select component. Or I can't find it in the dom.
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.
Okay, can we add a new one then?
|
||
export async function openSelect(element: Locator, options?: SelectOptionsType): Promise<Locator> { | ||
await element.getByRole('combobox').click(options); | ||
return element.page().getByLabel('Select options menu', { |
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.
Use selectors.components.Select.menu
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.
A slightly different selector got added in 11.4
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.
Okay, nice. Let's use than in +11.4
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 use the selector here too
packages/plugin-e2e/src/models/components/PanelEditOptionsGroup.ts
Outdated
Show resolved
Hide resolved
packages/plugin-e2e/tests/as-admin-user/panel/panelEdit.spec.ts
Outdated
Show resolved
Hide resolved
packages/plugin-e2e/src/models/components/PanelEditOptionsGroup.ts
Outdated
Show resolved
Hide resolved
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.
This is starting to look really good!
actual = await select | ||
.locator('div[class*="-grafana-select-value-container"]') | ||
.locator('div[class*="-singleValue"]') | ||
.innerText(options); |
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.
How? This is fine in older versions of Grafana, but we need a stable e2e-selector in Grafana to be future proof. Looks like there's a data-testid set here. Can we use that instead of the classnames?
} | ||
|
||
await this.ctx.page | ||
.locator('#grafana-portal-container') |
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.
Here as well. This is fine in older versions of Grafana, but now can we add a selector to this element?
|
||
export async function openSelect(element: Locator, options?: SelectOptionsType): Promise<Locator> { | ||
await element.getByRole('combobox').click(options); | ||
return element.page().getByLabel('Select options menu', { |
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.
Okay, nice. Let's use than in +11.4
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.
Great stuff! From my standpoint we're ready to ship this, but I think we should try to add proper selector for a few of the elements in Grafana first.
@@ -25,7 +25,7 @@ jobs: | |||
with: | |||
version-resolver-type: plugin-grafana-dependency | |||
grafana-dependency: '>=8.5.0' | |||
# limit: 0 # Uncomment to test all versions since 8.5.0. Useful when testing compatibility for new APIs. | |||
limit: 0 |
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.
now that all tests pass, we can revert the change in this file :)
actual = await select | ||
.locator('div[class*="-grafana-select-value-container"]') | ||
.locator('div[class*="-singleValue"]') | ||
.innerText(options); |
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.
Okay, can we add a new one then?
if (gte(this.ctx.grafanaVersion, '8.7.0')) { | ||
return this.ctx.page.locator('#grafana-portal-container'); | ||
} | ||
return this.ctx.page.locator('body > div').last(); |
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.
if you could add a new testid in Grafana and use it here that would be great!
|
||
export async function openSelect(element: Locator, options?: SelectOptionsType): Promise<Locator> { | ||
await element.getByRole('combobox').click(options); | ||
return element.page().getByLabel('Select options menu', { |
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 use the selector here too
} | ||
|
||
const id = await this.element.getAttribute('id', options); | ||
return this.group.locator(`label[for='${id}']`); |
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.
Nit: Maybe reverse the order here so that backwards compatibility becomes the "exception" rather than the default case. That way it will be easier to remove legacy code in the future.
|
||
private async getOption(selector: string, options?: SelectOptionsType): Promise<Locator> { | ||
const steps = selector.split('>').map((step) => step.trim()); | ||
const container = this.ctx.page.locator('div[class="rc-cascader-menus"]'); |
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 introduce a new data test id here too?
|
||
function getPanelContent(grafanaVersion: string, page: Page): Locator { | ||
if (gte(grafanaVersion, '9.5.0')) { | ||
return page.locator('div[class*="-panel-content"]'); |
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 use selectors.components.Panels.Panel.content
?
What this PR does / why we need it:
This PR introduce an abstraction to interact with the panel edit options. This is useful when developing panel plugins and you want to verify that different combinations of settings works as expected. The goal is to make it easier and more robust to write tests that interacts with the panel editor. This will also give us a layer where we can cater for differences in the panel editor between Grafana versions.
One example of this is the recent change of the switch component that changed role from checkbox to switch. Developers writing tests interacting with the switch directly would need to cater for this in the test which will make it harder and more complex to test your plugin against multiple versions of Grafana.
This is how the configuration of the panel edit options map into the test APIs.
Which issue(s) this PR fixes:
Fixes #
Special notes for your reviewer:
This is a suggestion and I would like to get more eyes on this to get better naming, structure and ways to interact with the panel editor in your test.
On thing that isn't that great is that while writing the tests you use the "labels" visible in the UI to select options to interact with. This is great for the options that the plugin adds because the plugin controls the labels that they add. But for the standard options or options that is added by Grafana we can still run into the problem that a plugin developer writes a test and interacts with the
Standard options
label. If we, for some reason, change that in a Grafana version the tests will stop working and the tests will not be able to run across multiple versions of Grafana.