-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Tests/labelmap color change tests #6054
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| import { | ||
| expect, | ||
| test, | ||
| visitStudy, | ||
| waitForViewportRenderCycle, | ||
| waitForViewportsRendered, | ||
| checkForScreenshot, | ||
| screenShotPaths, | ||
| } from './utils'; | ||
|
|
||
| const NEW_LABELMAP_SEGMENT_HEX = '#FF00FF'; | ||
| const NEW_LABELMAP_SEGMENT_HEX_CSS_RGB = 'rgb(255, 0, 255)'; | ||
|
|
||
| // Default color of segment 0 in the labelmap SEG study. | ||
| const LABELMAP_SEGMENT_0_DEFAULT_HEX = '#9D6CA2'; | ||
| const LABELMAP_SEGMENT_0_DEFAULT_CSS_RGB = 'rgb(157, 108, 162)'; | ||
|
|
||
| const STUDY_UID = '1.3.6.1.4.1.14519.5.2.1.256467663913010332776401703474716742458'; | ||
|
|
||
| test.beforeEach(async ({ page, leftPanelPageObject, DOMOverlayPageObject }) => { | ||
| await visitStudy(page, STUDY_UID, 'segmentation', 2000); | ||
|
|
||
| await leftPanelPageObject.loadSeriesByModality('SEG'); | ||
| await waitForViewportsRendered(page); | ||
|
|
||
| await expect(DOMOverlayPageObject.viewport.segmentationHydration.locator).toBeVisible(); | ||
| await DOMOverlayPageObject.viewport.segmentationHydration.yes.click(); | ||
| }); | ||
|
|
||
| test('opens the color edit popup when "Change Color" is clicked', async ({ | ||
| rightPanelPageObject, | ||
| DOMOverlayPageObject, | ||
| }) => { | ||
| const segment = rightPanelPageObject.labelMapSegmentationPanel.panel.nthSegment(0); | ||
| await segment.toggleVisibility(); | ||
|
|
||
| await segment.actions.openChangeColor(); | ||
|
|
||
| await expect(DOMOverlayPageObject.dialog.colorPicker.locator).toBeVisible(); | ||
| await expect(DOMOverlayPageObject.dialog.title).toHaveText('Segment Color'); | ||
| await expect(DOMOverlayPageObject.dialog.colorPicker.saveButton).toBeVisible(); | ||
| await expect(DOMOverlayPageObject.dialog.colorPicker.cancelButton).toBeVisible(); | ||
|
|
||
| await expect(DOMOverlayPageObject.dialog.colorPicker.hexInput).toHaveValue( | ||
| LABELMAP_SEGMENT_0_DEFAULT_HEX | ||
| ); | ||
| }); | ||
|
|
||
| test('changes the labelmap segment color when the user saves', async ({ | ||
| page, | ||
| rightPanelPageObject, | ||
| DOMOverlayPageObject, | ||
| viewportPageObject, | ||
| }) => { | ||
| await rightPanelPageObject.labelMapSegmentationPanel.segmentsVisibilityToggle.click(); | ||
| const segment = rightPanelPageObject.labelMapSegmentationPanel.panel.nthSegment(0); | ||
| await segment.toggleVisibility(); | ||
| await segment.click(); | ||
|
|
||
| await expect(segment.rowDataColorHex).toHaveCSS( | ||
| 'background-color', | ||
| LABELMAP_SEGMENT_0_DEFAULT_CSS_RGB | ||
| ); | ||
|
|
||
| const viewportPane = (await viewportPageObject.getById('default')).pane; | ||
| await checkForScreenshot({ | ||
| page, | ||
| locator: viewportPane, | ||
| screenshotPath: screenShotPaths.labelMapSegmentationColorChange.colorBeforeChange | ||
| }); | ||
|
|
||
| const colorChangeCycle = waitForViewportRenderCycle(page); | ||
| await segment.actions.changeColor(NEW_LABELMAP_SEGMENT_HEX); | ||
| await colorChangeCycle; | ||
|
Comment on lines
+72
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Prompt To Fix With AIThis is a comment left during a code review.
Path: tests/LabelMapSegmentationColorChange.spec.ts
Line: 72-74
Comment:
**Render-cycle promise captured before the action that triggers the render**
`waitForViewportRenderCycle(page)` is called before `segment.actions.changeColor(...)`, which itself performs four sequential interactions (open menu → click "Change Color" → fill hex → click Save). If the viewport renders for any intermediate reason during those steps — such as the color picker opening or focus events — the promise resolves early and `await colorChangeCycle` becomes a no-op. The screenshot that follows may then be captured before the actual color-change render completes. Capturing the promise _after_ `changeColor` resolves would remove this race.
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| await expect(DOMOverlayPageObject.dialog.colorPicker.locator).toBeHidden(); | ||
| await expect(segment.rowDataColorHex).toHaveCSS( | ||
| 'background-color', | ||
| NEW_LABELMAP_SEGMENT_HEX_CSS_RGB | ||
| ); | ||
|
|
||
| await checkForScreenshot({ | ||
| page, | ||
| locator: viewportPane, | ||
| screenshotPath: screenShotPaths.labelMapSegmentationColorChange.colorAfterChange | ||
| }); | ||
| }); | ||
|
|
||
| test('does not change the labelmap segment color when the user cancels', async ({ | ||
| page, | ||
| rightPanelPageObject, | ||
| DOMOverlayPageObject, | ||
| viewportPageObject, | ||
| }) => { | ||
| await rightPanelPageObject.labelMapSegmentationPanel.segmentsVisibilityToggle.click(); | ||
| const segment = rightPanelPageObject.labelMapSegmentationPanel.panel.nthSegment(0); | ||
| await segment.toggleVisibility(); | ||
| await segment.click(); | ||
|
|
||
| await expect(segment.rowDataColorHex).toHaveCSS( | ||
| 'background-color', | ||
| LABELMAP_SEGMENT_0_DEFAULT_CSS_RGB | ||
| ); | ||
|
|
||
| const viewportPane = (await viewportPageObject.getById('default')).pane; | ||
| await checkForScreenshot({ | ||
| page, | ||
| locator: viewportPane, | ||
| screenshotPath: screenShotPaths.labelMapSegmentationColorChange.colorBeforeCancel | ||
| }); | ||
|
|
||
| await segment.actions.cancelChangeColor(NEW_LABELMAP_SEGMENT_HEX); | ||
|
|
||
| await expect(DOMOverlayPageObject.dialog.colorPicker.locator).toBeHidden(); | ||
| await expect(segment.rowDataColorHex).toHaveCSS( | ||
| 'background-color', | ||
| LABELMAP_SEGMENT_0_DEFAULT_CSS_RGB | ||
| ); | ||
|
|
||
| await checkForScreenshot({ | ||
| page, | ||
| locator: viewportPane, | ||
| screenshotPath: screenShotPaths.labelMapSegmentationColorChange.colorAfterCancel | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,6 +60,39 @@ export class DOMOverlayPageObject { | |
| return new DicomTagBrowserPageObject(page); | ||
| }, | ||
|
|
||
| get colorPicker() { | ||
| const locator = page.getByTestId('color-picker-dialog'); | ||
| const hexInput = locator.getByLabel('hex'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Prompt To Fix With AIThis is a comment left during a code review.
Path: tests/pages/DOMOverlayPageObject.ts
Line: 65
Comment:
**`getByLabel('hex')` may not resolve to the ChromePicker input**
`react-color`'s `ChromePicker` uses a `<span>` as a visual label below the input — it is not a `<label>` element and carries no `for`/`aria-labelledby` association. Playwright's `getByLabel` relies on the accessibility tree; without a proper label association, this locator will either return no element or throw at runtime. A more reliable selector would be something like `locator.locator('input[id*="hex"], input[aria-label="hex"]')` or querying by the label-companion pattern that ChromePicker actually renders.
How can I resolve this? If you propose a fix, please make it concise.
Comment on lines
+64
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Answer: No. In react-color 2.19.3, the ChromePicker “hex” field uses EditableInput that renders an without an associated (so a by-label query for the label text should not match the input). The displayed hex value is rendered as a plain hex string coming from tinycolor’s toHexString, which includes the “#” prefix but does not guarantee uppercase (it reflects tinycolor’s output/casing for the given color). Evidence: 1) The EditableInput implementation in react-color renders a wrapper containing only an (with no element and no id/htmlFor linkage): the patched source shows render returning <input is="input" value={ this.props.value } readOnly/> .[1] 2) In the ChromePicker “hex” view, EditableInput is instantiated with label="hex" and value={ color.toHexString }, i.e., the input’s value comes from tinycolor toHexString and not from the label text.[2] So: - By-label query: should not match, because there is no associated element targeting the input.[1] - Value formatting: the value originates from toHexString (which includes the leading “#” prefix), but uppercase is not explicitly enforced in react-color; casing is determined by tinycolor.[2]
Citations: Fix
🤖 Prompt for AI Agents |
||
| const saveButton = page.getByTestId('color-picker-save-btn'); | ||
| const cancelButton = page.getByTestId('color-picker-cancel-btn'); | ||
| return { | ||
| locator, | ||
| hexInput, | ||
| saveButton, | ||
| cancelButton, | ||
| fillHex: async (hex: string) => { | ||
| await hexInput.fill(hex); | ||
| await hexInput.press('Enter'); | ||
| }, | ||
| save: async () => { | ||
| await saveButton.click(); | ||
| }, | ||
| cancel: async () => { | ||
| await cancelButton.click(); | ||
| }, | ||
| fillHexAndSave: async (hex: string) => { | ||
| await hexInput.fill(hex); | ||
| await hexInput.press('Enter'); | ||
| await saveButton.click(); | ||
| }, | ||
| fillHexAndCancel: async (hex: string) => { | ||
| await hexInput.fill(hex); | ||
| await hexInput.press('Enter'); | ||
| await cancelButton.click(); | ||
| }, | ||
| }; | ||
| }, | ||
|
|
||
| title: page.locator('[role="dialog"] h2'), | ||
| }; | ||
| } | ||
|
|
||
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.
#prefix mismatchChromePickertypically stores and displays the hex value as the 6-character string without the#(e.g.9D6CA2), while the#glyph is rendered as a static prefix outside the<input>. If that's the case here,toHaveValue('#9D6CA2')will always fail. The same concern applies tohexInput.fill('#FF00FF')infillHexAndSave/fillHexAndCancel— passing the#prefix into an input that doesn't expect it may silently produce an unexpected color state.Prompt To Fix With AI