You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@texera.apache.org by "mengw15 (via GitHub)" <gi...@apache.org> on 2026/08/05 11:12:06 UTC
[PR] test(frontend): extend UserVenvComponent template coverage [texera]
mengw15 opened a new pull request, #7334:
URL: https://github.com/apache/texera/pull/7334
### What changes were proposed in this PR?
Extends `user-venv.component.spec.ts` to render the template branches the existing
class-focused tests never exercised, taking the template from ~36% to 100%
(`user-venv.component.html` 102/102). 9 new tests plus one class-guard test:
- **list branches** — the empty-state message when there are no environments; a
row per environment (with the `(unnamed)` fallback); clicking a row opens its
draft; the delete icon fires `confirmDeletePve` and stops row-open propagation.
- **modal** — the Create button opens an empty draft; opening the modal renders
the form, the package header, one row per package, and the footer into the CDK
overlay; the add-package / delete-toggle / Save / Close controls are driven
through the DOM; the `nz-modal` cancel (X/mask) output closes the modal.
- a `saveEnvironment` no-op-without-draft guard test brings the class to 100% too.
Interactions go through the DOM (`By.css` + `triggerEventHandler` / native
`click`); the `nz-modal` body/footer render into the overlay, flushed with
`ApplicationRef.tick()`. The backing `WorkflowPveService` is mocked; no layout
assertions. No production code was changed.
### Any related issues, documentation, discussions?
Closes #7331.
### How was this PR tested?
`ng test --watch=false --include src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts`
— 33 passed, run 3× for determinism (the modal renders into the overlay). Coverage
(`--coverage`) confirms `user-venv.component.html` at 102/102 and the component
class at 89/89. The failure path was verified by breaking an assertion (red,
non-zero exit); eslint and prettier are clean.
### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.8 [1M context])
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@texera.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] test(frontend): extend UserVenvComponent template coverage [texera]
Posted by "codecov-commenter (via GitHub)" <gi...@apache.org>.
codecov-commenter commented on PR #7334:
URL: https://github.com/apache/texera/pull/7334#issuecomment-5192848542
## [Codecov](https://app.codecov.io/gh/apache/texera/pull/7334?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 83.45%. Comparing base ([`0f2cd49`](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)) to head ([`18fe57c`](https://app.codecov.io/gh/apache/texera/commit/18fe57c9e2fba157eee5787fc64fd753391fc758?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)).
<details><summary>Additional details and impacted files</summary>
```diff
@@ Coverage Diff @@
## main #7334 +/- ##
============================================
+ Coverage 83.36% 83.45% +0.09%
Complexity 4128 4128
============================================
Files 1166 1166
Lines 46428 46428
Branches 5174 5174
============================================
+ Hits 38705 38747 +42
+ Misses 6003 5962 -41
+ Partials 1720 1719 -1
```
| [Flag](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | | *Carryforward flag |
|---|---|---|---|
| [access-control-service](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `70.00% <ø> (ø)` | | Carriedforward from [0f2cd49](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
| [agent-service](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `83.65% <ø> (ø)` | | Carriedforward from [0f2cd49](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
| [amber](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `80.65% <ø> (ø)` | | Carriedforward from [0f2cd49](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
| [computing-unit-managing-service](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `43.60% <ø> (ø)` | | Carriedforward from [0f2cd49](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
| [config-service](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `65.97% <ø> (ø)` | | Carriedforward from [0f2cd49](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
| [file-service](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `69.05% <ø> (ø)` | | Carriedforward from [0f2cd49](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
| [frontend](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `84.23% <ø> (+0.20%)` | :arrow_up: | |
| [notebook-migration-service](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `78.89% <ø> (ø)` | | Carriedforward from [0f2cd49](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
| [pyamber](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `97.36% <ø> (ø)` | | Carriedforward from [0f2cd49](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
| [workflow-compiling-service](https://app.codecov.io/gh/apache/texera/pull/7334/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `26.31% <ø> (ø)` | | Carriedforward from [0f2cd49](https://app.codecov.io/gh/apache/texera/commit/0f2cd49c39781d683f35e02547da45fbe5adc3c3?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
*This pull request uses carry forward flags. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) to find out more.
</details>
[:umbrella: View full report in Codecov by Harness](https://app.codecov.io/gh/apache/texera/pull/7334?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).
<details><summary> :rocket: New features to boost your workflow: </summary>
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@texera.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] test(frontend): extend UserVenvComponent template coverage [texera]
Posted by "mengw15 (via GitHub)" <gi...@apache.org>.
mengw15 commented on code in PR #7334:
URL: https://github.com/apache/texera/pull/7334#discussion_r3720293408
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
Review Comment:
`overlay()` now goes through a null-asserting `q()` helper. Cross-test overlay contamination is already prevented — the top-level `afterEach` runs `fixture.destroy()` and clears `.cdk-overlay-container`, so every test opens into a clean overlay.
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
+
+ const openModalWith = (draft: Draft): HTMLElement => {
+ component.currentDraft = draft;
+ component.pveModalVisible = true;
+ flushOverlay();
+ return overlay();
+ };
+
+ const seedList = (records: UserPveRecord[]): void => {
+ pveServiceSpy.listUserPves.mockReturnValue(of(records));
+ fixture.detectChanges();
+ };
+
+ it("shows the empty-state message and no list when there are no environments", () => {
+ seedList([]);
+ const host = fixture.nativeElement as HTMLElement;
+ expect(host.querySelector(".python-env-page-empty")?.textContent).toContain("No environments yet");
+ expect(host.querySelector("ul.python-env-page-list")).toBeNull();
+ });
+
+ it("opens an empty draft modal from the Create button", () => {
+ fixture.detectChanges();
+ fixture.debugElement.query(By.css(".create-btn")).triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft).toEqual({ name: "", newPackages: [] });
+ });
+
+ it("renders a row per environment (with the unnamed fallback) and opens the row on click", () => {
+ seedList([
+ { veid: 1, name: "envA", packages: {} },
+ { veid: 2, name: "", packages: {} },
+ ] as UserPveRecord[]);
+
+ const rows = fixture.debugElement.queryAll(By.css("li.python-env-page-item"));
+ expect(rows.length).toBe(2);
+ expect((fixture.nativeElement as HTMLElement).textContent).toContain("(unnamed)");
+
+ rows[0].triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft?.name).toBe("envA");
+ });
+
+ it("fires confirmDeletePve from the row delete icon and stops row-open propagation", () => {
+ seedList([{ veid: 3, name: "envDel", packages: {} }] as UserPveRecord[]);
+ const stopPropagation = vi.fn();
+ fixture.debugElement.query(By.css(".python-env-delete-icon")).triggerEventHandler("click", { stopPropagation });
+ expect(stopPropagation).toHaveBeenCalled();
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
+ expect(component.pveModalVisible).toBe(false);
+ });
+
+ it("renders the modal form, package header, one row per package, and the footer when open", () => {
+ fixture.detectChanges();
+ const o = openModalWith({
+ name: "envForm",
+ newPackages: [
+ { name: "numpy", versionOp: "==", version: "1.2" },
+ { name: "pandas", versionOp: ">=", version: "2.0" },
+ ],
+ });
+
+ expect(o.querySelector(".ve-form")).not.toBeNull();
+ // header row (*ngIf newPackages.length > 0) + one row per package
+ expect(o.querySelectorAll(".package-row").length).toBe(3);
+ expect(o.querySelector(".add-btn button")).not.toBeNull();
+ expect(o.querySelectorAll(".footer-all button").length).toBe(2);
+ });
+
+ it("drives the modal package controls and the Save footer button through the DOM", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envDrive", newPackages: [{ name: "x", versionOp: "==", version: "1" }] });
+
+ (o.querySelector(".add-btn button") as HTMLButtonElement).click();
Review Comment:
Routed through a `q<E>(root, selector)` helper that asserts the element exists first, so a missing selector fails as `expected to find "…"` instead of a null dereference.
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
+
+ const openModalWith = (draft: Draft): HTMLElement => {
+ component.currentDraft = draft;
+ component.pveModalVisible = true;
+ flushOverlay();
+ return overlay();
+ };
+
+ const seedList = (records: UserPveRecord[]): void => {
+ pveServiceSpy.listUserPves.mockReturnValue(of(records));
+ fixture.detectChanges();
+ };
+
+ it("shows the empty-state message and no list when there are no environments", () => {
+ seedList([]);
+ const host = fixture.nativeElement as HTMLElement;
+ expect(host.querySelector(".python-env-page-empty")?.textContent).toContain("No environments yet");
+ expect(host.querySelector("ul.python-env-page-list")).toBeNull();
+ });
+
+ it("opens an empty draft modal from the Create button", () => {
+ fixture.detectChanges();
+ fixture.debugElement.query(By.css(".create-btn")).triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft).toEqual({ name: "", newPackages: [] });
+ });
+
+ it("renders a row per environment (with the unnamed fallback) and opens the row on click", () => {
+ seedList([
+ { veid: 1, name: "envA", packages: {} },
+ { veid: 2, name: "", packages: {} },
+ ] as UserPveRecord[]);
+
+ const rows = fixture.debugElement.queryAll(By.css("li.python-env-page-item"));
+ expect(rows.length).toBe(2);
+ expect((fixture.nativeElement as HTMLElement).textContent).toContain("(unnamed)");
+
+ rows[0].triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft?.name).toBe("envA");
+ });
+
+ it("fires confirmDeletePve from the row delete icon and stops row-open propagation", () => {
+ seedList([{ veid: 3, name: "envDel", packages: {} }] as UserPveRecord[]);
+ const stopPropagation = vi.fn();
+ fixture.debugElement.query(By.css(".python-env-delete-icon")).triggerEventHandler("click", { stopPropagation });
+ expect(stopPropagation).toHaveBeenCalled();
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
+ expect(component.pveModalVisible).toBe(false);
+ });
+
+ it("renders the modal form, package header, one row per package, and the footer when open", () => {
+ fixture.detectChanges();
+ const o = openModalWith({
+ name: "envForm",
+ newPackages: [
+ { name: "numpy", versionOp: "==", version: "1.2" },
+ { name: "pandas", versionOp: ">=", version: "2.0" },
+ ],
+ });
+
+ expect(o.querySelector(".ve-form")).not.toBeNull();
+ // header row (*ngIf newPackages.length > 0) + one row per package
+ expect(o.querySelectorAll(".package-row").length).toBe(3);
+ expect(o.querySelector(".add-btn button")).not.toBeNull();
+ expect(o.querySelectorAll(".footer-all button").length).toBe(2);
+ });
+
+ it("drives the modal package controls and the Save footer button through the DOM", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envDrive", newPackages: [{ name: "x", versionOp: "==", version: "1" }] });
+
+ (o.querySelector(".add-btn button") as HTMLButtonElement).click();
+ flushOverlay();
+ expect(component.currentDraft?.newPackages.length).toBe(2);
+
+ (o.querySelector(".package-row .user-package-inputs button") as HTMLButtonElement).click();
Review Comment:
Routed through a `q<E>(root, selector)` helper that asserts the element exists first, so a missing selector fails as `expected to find "…"` instead of a null dereference.
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
+
+ const openModalWith = (draft: Draft): HTMLElement => {
+ component.currentDraft = draft;
+ component.pveModalVisible = true;
+ flushOverlay();
+ return overlay();
+ };
+
+ const seedList = (records: UserPveRecord[]): void => {
+ pveServiceSpy.listUserPves.mockReturnValue(of(records));
+ fixture.detectChanges();
+ };
+
+ it("shows the empty-state message and no list when there are no environments", () => {
+ seedList([]);
+ const host = fixture.nativeElement as HTMLElement;
+ expect(host.querySelector(".python-env-page-empty")?.textContent).toContain("No environments yet");
+ expect(host.querySelector("ul.python-env-page-list")).toBeNull();
+ });
+
+ it("opens an empty draft modal from the Create button", () => {
+ fixture.detectChanges();
+ fixture.debugElement.query(By.css(".create-btn")).triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft).toEqual({ name: "", newPackages: [] });
+ });
+
+ it("renders a row per environment (with the unnamed fallback) and opens the row on click", () => {
+ seedList([
+ { veid: 1, name: "envA", packages: {} },
+ { veid: 2, name: "", packages: {} },
+ ] as UserPveRecord[]);
+
+ const rows = fixture.debugElement.queryAll(By.css("li.python-env-page-item"));
+ expect(rows.length).toBe(2);
+ expect((fixture.nativeElement as HTMLElement).textContent).toContain("(unnamed)");
+
+ rows[0].triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft?.name).toBe("envA");
+ });
+
+ it("fires confirmDeletePve from the row delete icon and stops row-open propagation", () => {
+ seedList([{ veid: 3, name: "envDel", packages: {} }] as UserPveRecord[]);
+ const stopPropagation = vi.fn();
+ fixture.debugElement.query(By.css(".python-env-delete-icon")).triggerEventHandler("click", { stopPropagation });
+ expect(stopPropagation).toHaveBeenCalled();
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
+ expect(component.pveModalVisible).toBe(false);
+ });
+
+ it("renders the modal form, package header, one row per package, and the footer when open", () => {
+ fixture.detectChanges();
+ const o = openModalWith({
+ name: "envForm",
+ newPackages: [
+ { name: "numpy", versionOp: "==", version: "1.2" },
+ { name: "pandas", versionOp: ">=", version: "2.0" },
+ ],
+ });
+
+ expect(o.querySelector(".ve-form")).not.toBeNull();
+ // header row (*ngIf newPackages.length > 0) + one row per package
+ expect(o.querySelectorAll(".package-row").length).toBe(3);
+ expect(o.querySelector(".add-btn button")).not.toBeNull();
+ expect(o.querySelectorAll(".footer-all button").length).toBe(2);
+ });
+
+ it("drives the modal package controls and the Save footer button through the DOM", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envDrive", newPackages: [{ name: "x", versionOp: "==", version: "1" }] });
+
+ (o.querySelector(".add-btn button") as HTMLButtonElement).click();
+ flushOverlay();
+ expect(component.currentDraft?.newPackages.length).toBe(2);
+
+ (o.querySelector(".package-row .user-package-inputs button") as HTMLButtonElement).click();
+ expect(component.currentDraft?.newPackages[0].deleteToggle).toBe(true);
+
+ (o.querySelectorAll(".footer-all button")[1] as HTMLButtonElement).click();
Review Comment:
Switched to a `footerButton(root, label)` helper that matches by button text (`"Save"` / `"Close"`), so the tests survive footer-button reordering.
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
+
+ const openModalWith = (draft: Draft): HTMLElement => {
+ component.currentDraft = draft;
+ component.pveModalVisible = true;
+ flushOverlay();
+ return overlay();
+ };
+
+ const seedList = (records: UserPveRecord[]): void => {
+ pveServiceSpy.listUserPves.mockReturnValue(of(records));
+ fixture.detectChanges();
+ };
+
+ it("shows the empty-state message and no list when there are no environments", () => {
+ seedList([]);
+ const host = fixture.nativeElement as HTMLElement;
+ expect(host.querySelector(".python-env-page-empty")?.textContent).toContain("No environments yet");
+ expect(host.querySelector("ul.python-env-page-list")).toBeNull();
+ });
+
+ it("opens an empty draft modal from the Create button", () => {
+ fixture.detectChanges();
+ fixture.debugElement.query(By.css(".create-btn")).triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft).toEqual({ name: "", newPackages: [] });
+ });
+
+ it("renders a row per environment (with the unnamed fallback) and opens the row on click", () => {
+ seedList([
+ { veid: 1, name: "envA", packages: {} },
+ { veid: 2, name: "", packages: {} },
+ ] as UserPveRecord[]);
+
+ const rows = fixture.debugElement.queryAll(By.css("li.python-env-page-item"));
+ expect(rows.length).toBe(2);
+ expect((fixture.nativeElement as HTMLElement).textContent).toContain("(unnamed)");
+
+ rows[0].triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft?.name).toBe("envA");
+ });
+
+ it("fires confirmDeletePve from the row delete icon and stops row-open propagation", () => {
+ seedList([{ veid: 3, name: "envDel", packages: {} }] as UserPveRecord[]);
+ const stopPropagation = vi.fn();
+ fixture.debugElement.query(By.css(".python-env-delete-icon")).triggerEventHandler("click", { stopPropagation });
+ expect(stopPropagation).toHaveBeenCalled();
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
+ expect(component.pveModalVisible).toBe(false);
+ });
+
+ it("renders the modal form, package header, one row per package, and the footer when open", () => {
+ fixture.detectChanges();
+ const o = openModalWith({
+ name: "envForm",
+ newPackages: [
+ { name: "numpy", versionOp: "==", version: "1.2" },
+ { name: "pandas", versionOp: ">=", version: "2.0" },
+ ],
+ });
+
+ expect(o.querySelector(".ve-form")).not.toBeNull();
+ // header row (*ngIf newPackages.length > 0) + one row per package
+ expect(o.querySelectorAll(".package-row").length).toBe(3);
+ expect(o.querySelector(".add-btn button")).not.toBeNull();
+ expect(o.querySelectorAll(".footer-all button").length).toBe(2);
+ });
+
+ it("drives the modal package controls and the Save footer button through the DOM", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envDrive", newPackages: [{ name: "x", versionOp: "==", version: "1" }] });
+
+ (o.querySelector(".add-btn button") as HTMLButtonElement).click();
+ flushOverlay();
+ expect(component.currentDraft?.newPackages.length).toBe(2);
+
+ (o.querySelector(".package-row .user-package-inputs button") as HTMLButtonElement).click();
+ expect(component.currentDraft?.newPackages[0].deleteToggle).toBe(true);
+
+ (o.querySelectorAll(".footer-all button")[1] as HTMLButtonElement).click();
+ expect(pveServiceSpy.savePve).toHaveBeenCalledWith("envDrive", {});
+ });
+
+ it("closes the modal from the footer Close button", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envClose", newPackages: [] });
+ (o.querySelectorAll(".footer-all button")[0] as HTMLButtonElement).click();
Review Comment:
Switched to a `footerButton(root, label)` helper that matches by button text (`"Save"` / `"Close"`), so the tests survive footer-button reordering.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@texera.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] test(frontend): extend UserVenvComponent template coverage [texera]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #7334:
URL: https://github.com/apache/texera/pull/7334#issuecomment-5191346001
<!-- texera-reviewer-suggestion -->
### Automated Reviewer Suggestions
Based on the `git blame` history of the changed files, we recommend the following reviewers:
- **Contributors with relevant context:** `@aglinxinyuan`
You can notify them by mentioning `@aglinxinyuan` in a comment.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@texera.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] test(frontend): extend UserVenvComponent template coverage [texera]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #7334:
URL: https://github.com/apache/texera/pull/7334#discussion_r3720165449
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
Review Comment:
This helper unsafely casts the result of `document.querySelector(...)` to `HTMLElement`. If the overlay container isn’t present (or is removed between tests), this becomes `null` at runtime and will fail with a cryptic error later. Prefer using Angular CDK’s `OverlayContainer` test utility (or at minimum assert non-null before casting) and consider cleaning the overlay container in `afterEach` to avoid cross-test contamination.
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
+
+ const openModalWith = (draft: Draft): HTMLElement => {
+ component.currentDraft = draft;
+ component.pveModalVisible = true;
+ flushOverlay();
+ return overlay();
+ };
+
+ const seedList = (records: UserPveRecord[]): void => {
+ pveServiceSpy.listUserPves.mockReturnValue(of(records));
+ fixture.detectChanges();
+ };
+
+ it("shows the empty-state message and no list when there are no environments", () => {
+ seedList([]);
+ const host = fixture.nativeElement as HTMLElement;
+ expect(host.querySelector(".python-env-page-empty")?.textContent).toContain("No environments yet");
+ expect(host.querySelector("ul.python-env-page-list")).toBeNull();
+ });
+
+ it("opens an empty draft modal from the Create button", () => {
+ fixture.detectChanges();
+ fixture.debugElement.query(By.css(".create-btn")).triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft).toEqual({ name: "", newPackages: [] });
+ });
+
+ it("renders a row per environment (with the unnamed fallback) and opens the row on click", () => {
+ seedList([
+ { veid: 1, name: "envA", packages: {} },
+ { veid: 2, name: "", packages: {} },
+ ] as UserPveRecord[]);
+
+ const rows = fixture.debugElement.queryAll(By.css("li.python-env-page-item"));
+ expect(rows.length).toBe(2);
+ expect((fixture.nativeElement as HTMLElement).textContent).toContain("(unnamed)");
+
+ rows[0].triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft?.name).toBe("envA");
+ });
+
+ it("fires confirmDeletePve from the row delete icon and stops row-open propagation", () => {
+ seedList([{ veid: 3, name: "envDel", packages: {} }] as UserPveRecord[]);
+ const stopPropagation = vi.fn();
+ fixture.debugElement.query(By.css(".python-env-delete-icon")).triggerEventHandler("click", { stopPropagation });
+ expect(stopPropagation).toHaveBeenCalled();
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
+ expect(component.pveModalVisible).toBe(false);
+ });
+
+ it("renders the modal form, package header, one row per package, and the footer when open", () => {
+ fixture.detectChanges();
+ const o = openModalWith({
+ name: "envForm",
+ newPackages: [
+ { name: "numpy", versionOp: "==", version: "1.2" },
+ { name: "pandas", versionOp: ">=", version: "2.0" },
+ ],
+ });
+
+ expect(o.querySelector(".ve-form")).not.toBeNull();
+ // header row (*ngIf newPackages.length > 0) + one row per package
+ expect(o.querySelectorAll(".package-row").length).toBe(3);
+ expect(o.querySelector(".add-btn button")).not.toBeNull();
+ expect(o.querySelectorAll(".footer-all button").length).toBe(2);
+ });
+
+ it("drives the modal package controls and the Save footer button through the DOM", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envDrive", newPackages: [{ name: "x", versionOp: "==", version: "1" }] });
+
+ (o.querySelector(".add-btn button") as HTMLButtonElement).click();
+ flushOverlay();
+ expect(component.currentDraft?.newPackages.length).toBe(2);
+
+ (o.querySelector(".package-row .user-package-inputs button") as HTMLButtonElement).click();
+ expect(component.currentDraft?.newPackages[0].deleteToggle).toBe(true);
+
+ (o.querySelectorAll(".footer-all button")[1] as HTMLButtonElement).click();
+ expect(pveServiceSpy.savePve).toHaveBeenCalledWith("envDrive", {});
+ });
+
+ it("closes the modal from the footer Close button", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envClose", newPackages: [] });
+ (o.querySelectorAll(".footer-all button")[0] as HTMLButtonElement).click();
Review Comment:
Index-based selection of footer buttons (`[1]` for Save, `[0]` for Close) is brittle and can break if button order changes (or if another button is added). Use a more stable selector (e.g., `data-testid`, an `aria-label`, or matching by button text/content) so the tests fail only when behavior changes, not when markup is rearranged.
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
+
+ const openModalWith = (draft: Draft): HTMLElement => {
+ component.currentDraft = draft;
+ component.pveModalVisible = true;
+ flushOverlay();
+ return overlay();
+ };
+
+ const seedList = (records: UserPveRecord[]): void => {
+ pveServiceSpy.listUserPves.mockReturnValue(of(records));
+ fixture.detectChanges();
+ };
+
+ it("shows the empty-state message and no list when there are no environments", () => {
+ seedList([]);
+ const host = fixture.nativeElement as HTMLElement;
+ expect(host.querySelector(".python-env-page-empty")?.textContent).toContain("No environments yet");
+ expect(host.querySelector("ul.python-env-page-list")).toBeNull();
+ });
+
+ it("opens an empty draft modal from the Create button", () => {
+ fixture.detectChanges();
+ fixture.debugElement.query(By.css(".create-btn")).triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft).toEqual({ name: "", newPackages: [] });
+ });
+
+ it("renders a row per environment (with the unnamed fallback) and opens the row on click", () => {
+ seedList([
+ { veid: 1, name: "envA", packages: {} },
+ { veid: 2, name: "", packages: {} },
+ ] as UserPveRecord[]);
+
+ const rows = fixture.debugElement.queryAll(By.css("li.python-env-page-item"));
+ expect(rows.length).toBe(2);
+ expect((fixture.nativeElement as HTMLElement).textContent).toContain("(unnamed)");
+
+ rows[0].triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft?.name).toBe("envA");
+ });
+
+ it("fires confirmDeletePve from the row delete icon and stops row-open propagation", () => {
+ seedList([{ veid: 3, name: "envDel", packages: {} }] as UserPveRecord[]);
+ const stopPropagation = vi.fn();
+ fixture.debugElement.query(By.css(".python-env-delete-icon")).triggerEventHandler("click", { stopPropagation });
+ expect(stopPropagation).toHaveBeenCalled();
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
+ expect(component.pveModalVisible).toBe(false);
+ });
+
+ it("renders the modal form, package header, one row per package, and the footer when open", () => {
+ fixture.detectChanges();
+ const o = openModalWith({
+ name: "envForm",
+ newPackages: [
+ { name: "numpy", versionOp: "==", version: "1.2" },
+ { name: "pandas", versionOp: ">=", version: "2.0" },
+ ],
+ });
+
+ expect(o.querySelector(".ve-form")).not.toBeNull();
+ // header row (*ngIf newPackages.length > 0) + one row per package
+ expect(o.querySelectorAll(".package-row").length).toBe(3);
+ expect(o.querySelector(".add-btn button")).not.toBeNull();
+ expect(o.querySelectorAll(".footer-all button").length).toBe(2);
+ });
+
+ it("drives the modal package controls and the Save footer button through the DOM", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envDrive", newPackages: [{ name: "x", versionOp: "==", version: "1" }] });
+
+ (o.querySelector(".add-btn button") as HTMLButtonElement).click();
+ flushOverlay();
+ expect(component.currentDraft?.newPackages.length).toBe(2);
+
+ (o.querySelector(".package-row .user-package-inputs button") as HTMLButtonElement).click();
+ expect(component.currentDraft?.newPackages[0].deleteToggle).toBe(true);
+
+ (o.querySelectorAll(".footer-all button")[1] as HTMLButtonElement).click();
Review Comment:
Index-based selection of footer buttons (`[1]` for Save, `[0]` for Close) is brittle and can break if button order changes (or if another button is added). Use a more stable selector (e.g., `data-testid`, an `aria-label`, or matching by button text/content) so the tests fail only when behavior changes, not when markup is rearranged.
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
+
+ const openModalWith = (draft: Draft): HTMLElement => {
+ component.currentDraft = draft;
+ component.pveModalVisible = true;
+ flushOverlay();
+ return overlay();
+ };
+
+ const seedList = (records: UserPveRecord[]): void => {
+ pveServiceSpy.listUserPves.mockReturnValue(of(records));
+ fixture.detectChanges();
+ };
+
+ it("shows the empty-state message and no list when there are no environments", () => {
+ seedList([]);
+ const host = fixture.nativeElement as HTMLElement;
+ expect(host.querySelector(".python-env-page-empty")?.textContent).toContain("No environments yet");
+ expect(host.querySelector("ul.python-env-page-list")).toBeNull();
+ });
+
+ it("opens an empty draft modal from the Create button", () => {
+ fixture.detectChanges();
+ fixture.debugElement.query(By.css(".create-btn")).triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft).toEqual({ name: "", newPackages: [] });
+ });
+
+ it("renders a row per environment (with the unnamed fallback) and opens the row on click", () => {
+ seedList([
+ { veid: 1, name: "envA", packages: {} },
+ { veid: 2, name: "", packages: {} },
+ ] as UserPveRecord[]);
+
+ const rows = fixture.debugElement.queryAll(By.css("li.python-env-page-item"));
+ expect(rows.length).toBe(2);
+ expect((fixture.nativeElement as HTMLElement).textContent).toContain("(unnamed)");
+
+ rows[0].triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft?.name).toBe("envA");
+ });
+
+ it("fires confirmDeletePve from the row delete icon and stops row-open propagation", () => {
+ seedList([{ veid: 3, name: "envDel", packages: {} }] as UserPveRecord[]);
+ const stopPropagation = vi.fn();
+ fixture.debugElement.query(By.css(".python-env-delete-icon")).triggerEventHandler("click", { stopPropagation });
+ expect(stopPropagation).toHaveBeenCalled();
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
+ expect(component.pveModalVisible).toBe(false);
+ });
+
+ it("renders the modal form, package header, one row per package, and the footer when open", () => {
+ fixture.detectChanges();
+ const o = openModalWith({
+ name: "envForm",
+ newPackages: [
+ { name: "numpy", versionOp: "==", version: "1.2" },
+ { name: "pandas", versionOp: ">=", version: "2.0" },
+ ],
+ });
+
+ expect(o.querySelector(".ve-form")).not.toBeNull();
+ // header row (*ngIf newPackages.length > 0) + one row per package
+ expect(o.querySelectorAll(".package-row").length).toBe(3);
+ expect(o.querySelector(".add-btn button")).not.toBeNull();
+ expect(o.querySelectorAll(".footer-all button").length).toBe(2);
+ });
+
+ it("drives the modal package controls and the Save footer button through the DOM", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envDrive", newPackages: [{ name: "x", versionOp: "==", version: "1" }] });
+
+ (o.querySelector(".add-btn button") as HTMLButtonElement).click();
Review Comment:
These assertions rely on `querySelector(...) as HTMLButtonElement` and will throw if the selector doesn’t match (e.g., template refactor), producing less actionable failures. Prefer asserting the element exists before interacting (or use `fixture.debugElement.query(...)` with expectations) so failures clearly indicate 'element not found' vs. a null dereference.
##########
frontend/src/app/dashboard/component/user/user-venv/user-venv.component.spec.ts:
##########
@@ -377,4 +387,116 @@ describe("UserVenvComponent", () => {
expect(component.trackByVeid(1, { name: "", newPackages: [] })).toBeUndefined();
});
});
+
+ // The class is well covered above; these exercise the template itself — the list
+ // branches and the nz-modal body/footer, which render into the CDK overlay.
+ describe("template rendering", () => {
+ type Draft = NonNullable<UserVenvComponent["currentDraft"]>;
+
+ // nz-modal renders into the overlay attached to ApplicationRef, so tick() after
+ // detectChanges to flush its embedded view.
+ const flushOverlay = (): void => {
+ fixture.detectChanges();
+ TestBed.inject(ApplicationRef).tick();
+ };
+ const overlay = (): HTMLElement => document.querySelector(".cdk-overlay-container") as HTMLElement;
+
+ const openModalWith = (draft: Draft): HTMLElement => {
+ component.currentDraft = draft;
+ component.pveModalVisible = true;
+ flushOverlay();
+ return overlay();
+ };
+
+ const seedList = (records: UserPveRecord[]): void => {
+ pveServiceSpy.listUserPves.mockReturnValue(of(records));
+ fixture.detectChanges();
+ };
+
+ it("shows the empty-state message and no list when there are no environments", () => {
+ seedList([]);
+ const host = fixture.nativeElement as HTMLElement;
+ expect(host.querySelector(".python-env-page-empty")?.textContent).toContain("No environments yet");
+ expect(host.querySelector("ul.python-env-page-list")).toBeNull();
+ });
+
+ it("opens an empty draft modal from the Create button", () => {
+ fixture.detectChanges();
+ fixture.debugElement.query(By.css(".create-btn")).triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft).toEqual({ name: "", newPackages: [] });
+ });
+
+ it("renders a row per environment (with the unnamed fallback) and opens the row on click", () => {
+ seedList([
+ { veid: 1, name: "envA", packages: {} },
+ { veid: 2, name: "", packages: {} },
+ ] as UserPveRecord[]);
+
+ const rows = fixture.debugElement.queryAll(By.css("li.python-env-page-item"));
+ expect(rows.length).toBe(2);
+ expect((fixture.nativeElement as HTMLElement).textContent).toContain("(unnamed)");
+
+ rows[0].triggerEventHandler("click", {});
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.currentDraft?.name).toBe("envA");
+ });
+
+ it("fires confirmDeletePve from the row delete icon and stops row-open propagation", () => {
+ seedList([{ veid: 3, name: "envDel", packages: {} }] as UserPveRecord[]);
+ const stopPropagation = vi.fn();
+ fixture.debugElement.query(By.css(".python-env-delete-icon")).triggerEventHandler("click", { stopPropagation });
+ expect(stopPropagation).toHaveBeenCalled();
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
+ expect(component.pveModalVisible).toBe(false);
+ });
+
+ it("renders the modal form, package header, one row per package, and the footer when open", () => {
+ fixture.detectChanges();
+ const o = openModalWith({
+ name: "envForm",
+ newPackages: [
+ { name: "numpy", versionOp: "==", version: "1.2" },
+ { name: "pandas", versionOp: ">=", version: "2.0" },
+ ],
+ });
+
+ expect(o.querySelector(".ve-form")).not.toBeNull();
+ // header row (*ngIf newPackages.length > 0) + one row per package
+ expect(o.querySelectorAll(".package-row").length).toBe(3);
+ expect(o.querySelector(".add-btn button")).not.toBeNull();
+ expect(o.querySelectorAll(".footer-all button").length).toBe(2);
+ });
+
+ it("drives the modal package controls and the Save footer button through the DOM", () => {
+ fixture.detectChanges();
+ const o = openModalWith({ name: "envDrive", newPackages: [{ name: "x", versionOp: "==", version: "1" }] });
+
+ (o.querySelector(".add-btn button") as HTMLButtonElement).click();
+ flushOverlay();
+ expect(component.currentDraft?.newPackages.length).toBe(2);
+
+ (o.querySelector(".package-row .user-package-inputs button") as HTMLButtonElement).click();
Review Comment:
These assertions rely on `querySelector(...) as HTMLButtonElement` and will throw if the selector doesn’t match (e.g., template refactor), producing less actionable failures. Prefer asserting the element exists before interacting (or use `fixture.debugElement.query(...)` with expectations) so failures clearly indicate 'element not found' vs. a null dereference.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@texera.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] test(frontend): extend UserVenvComponent template coverage [texera]
Posted by "aglinxinyuan (via GitHub)" <gi...@apache.org>.
aglinxinyuan merged PR #7334:
URL: https://github.com/apache/texera/pull/7334
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@texera.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org