This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-7472-f00b4ca9a8fa52bd7892b3f50619e2bfffb80936 in repository https://gitbox.apache.org/repos/asf/texera.git
commit dafccec7990fb3aec6b2b07b919051eccc115176 Author: Meng Wang <[email protected]> AuthorDate: Sun Aug 9 16:09:25 2026 -0700 test(frontend): extend DatasetFileSelectorComponent template coverage (#7472) ### What changes were proposed in this PR? The existing tests call `onClickOpenFileSelectionModal` directly, so the template had never been rendered — it sat at 1/10 statements. Adds 6 tests that render it in each state it switches on, taking `dataset-file-selector.component.html` to 10/10 with no uncovered branches (and the class to 16/16, its last branch being the component's own instantiation). - **enabled, with a path** — the path is shown and the Select File button is offered. - **enabled, no path yet** — the input is withheld until there is something to show; only the button renders. - **disabled** — no picker to write the path, so the empty input is shown instead and the button is gone. - **the button** — clicking it opens the selection modal; triggering it while the flag is off does not, which is what the handler's own `isFileSelectionEnabled &&` guard is for (the `*ngIf` normally removes the button, so the test disables the flag without re-rendering to reach that guard). The GUI-config double moved from an inline provider literal to a variable so a test can flip `selectingFilesFromDatasetsEnabled`; its default is unchanged. No production code was changed. **One test pins a defect rather than the intent.** `[readOnly]="isFileSelectionEnabled"` does not make the input read-only: the camelCase name misses `NzInputDirective`'s `readonly` input, so it lands on the DOM property, and the directive's `[attr.readonly]="readonly() || null"` host binding then clears the attribute and resets the property. Verified both ways locally — with `[readOnly]` the rendered input reports `readOnly: false, attr: null`; renaming it to `[readonly]` gives `readOnly: true, attr: true`. So the path the picker is meant to own can currently be typed over. The test asserts the real behaviour with a comment saying what to flip when the one-word fix lands; the fix itself is a production change and out of scope for a coverage PR. ### Any related issues, documentation, discussions? Closes #7466. ### How was this PR tested? `ng test --watch=false --include src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.spec.ts` — 11 passed (5 existing + 6 new), run 3x for determinism. Coverage (`--coverage`) confirms `dataset-file-selector.component.html` at 10/10 statements with no uncovered branches. 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]) --- .../dataset-file-selector.component.spec.ts | 83 +++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.spec.ts b/frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.spec.ts index cb2874a643..24a2214bfe 100644 --- a/frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.spec.ts +++ b/frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.spec.ts @@ -19,6 +19,7 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { FormControl } from "@angular/forms"; +import { By } from "@angular/platform-browser"; import { FieldTypeConfig } from "@ngx-formly/core"; import { of } from "rxjs"; import { NzModalService } from "ng-zorro-antd/modal"; @@ -33,6 +34,9 @@ describe("DatasetFileSelectorComponent", () => { // reads `afterClose` off the returned modal ref. Each test overrides what // `afterClose` emits via `mockReturnValue`. let modalServiceSpy: { create: ReturnType<typeof vi.fn> }; + // Held in a variable rather than inlined into the provider so a test can flip the + // flag; `isFileSelectionEnabled` reads straight through to it. + let guiConfigStub: { env: { selectingFilesFromDatasetsEnabled: boolean } }; // Attach a fresh FormControl with a known starting value, since the component // extends FieldType and reads/writes through `this.formControl`. @@ -44,13 +48,14 @@ describe("DatasetFileSelectorComponent", () => { beforeEach(async () => { modalServiceSpy = { create: vi.fn() }; + guiConfigStub = { env: { selectingFilesFromDatasetsEnabled: true } }; await TestBed.configureTestingModule({ imports: [DatasetFileSelectorComponent], providers: [ { provide: NzModalService, useValue: modalServiceSpy }, { provide: WorkflowActionService, useValue: {} }, - { provide: GuiConfigService, useValue: { env: { selectingFilesFromDatasetsEnabled: true } } }, + { provide: GuiConfigService, useValue: guiConfigStub }, ], }).compileComponents(); @@ -92,4 +97,80 @@ describe("DatasetFileSelectorComponent", () => { it("exposes isFileSelectionEnabled from the GUI config", () => { expect(component.isFileSelectionEnabled).toBe(true); }); + + /** + * The tests above call `onClickOpenFileSelectionModal` directly, so the template had never + * been rendered. What it decides: whether the path is shown at all, whether it can be typed + * into, and whether the picker is offered — all of which turn on the deployment-level + * `selectingFilesFromDatasetsEnabled` flag. + */ + describe("template rendering", () => { + /** Renders the field with the given path, under the given deployment flag. */ + function render(value: string, fileSelectionEnabled = true): void { + guiConfigStub.env.selectingFilesFromDatasetsEnabled = fileSelectionEnabled; + setFormControl(value); + fixture.detectChanges(); + } + + const input = () => fixture.debugElement.query(By.css("input")); + const selectFileButton = () => fixture.debugElement.query(By.css("button")); + + it("shows the chosen path alongside the Select File button", () => { + render("/dataset/data.csv"); + + expect(input().nativeElement.value).toBe("/dataset/data.csv"); + expect(selectFileButton().nativeElement.textContent.trim()).toBe("Select File"); + }); + + it("leaves the path input editable even with file selection enabled", () => { + render("/dataset/data.csv"); + + // Characterizing a defect rather than asserting the intent: the template's + // `[readOnly]` is camelCase, so it misses NzInputDirective's `readonly` input and + // lands on the DOM property; the directive then host-binds + // `[attr.readonly]="readonly() || null"`, which clears the attribute and resets the + // property. So the path the picker is meant to own can still be typed over. + // Spelling the binding `[readonly]` makes it take effect — a production change, out + // of scope here. Flip this expectation to `true` when that lands. + expect(input().nativeElement.readOnly).toBe(false); + }); + + it("shows only the Select File button until a path has been chosen", () => { + render(""); + + expect(input()).toBeNull(); + expect(selectFileButton()).not.toBeNull(); + }); + + it("falls back to a typeable path input when file selection is disabled", () => { + // No picker to write the path, so the empty input has to be shown and be editable. + render("", false); + + expect(input()).not.toBeNull(); + expect(input().nativeElement.readOnly).toBe(false); + expect(selectFileButton()).toBeNull(); + }); + + it("opens the selection modal when the Select File button is clicked", () => { + modalServiceSpy.create.mockReturnValue({ afterClose: of("/dataset/data.csv") }); + render(""); + + selectFileButton().triggerEventHandler("click", null); + + expect(modalServiceSpy.create).toHaveBeenCalledTimes(1); + }); + + it("does not open the modal if the button is triggered while file selection is disabled", () => { + render(""); + const button = selectFileButton(); + + // The *ngIf normally removes the button, so reach the handler's own guard by disabling + // the flag without re-rendering. It is what stops a stale button from opening a picker + // the deployment has turned off. + guiConfigStub.env.selectingFilesFromDatasetsEnabled = false; + button.triggerEventHandler("click", null); + + expect(modalServiceSpy.create).not.toHaveBeenCalled(); + }); + }); });
