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-6681-d3aa22e93fecce420595f7b999be60a2bc2ea924 in repository https://gitbox.apache.org/repos/asf/texera.git
commit f3cc2ddc4380684e0ce3aa0122c23f439f9355c9 Author: Matthew B. <[email protected]> AuthorDate: Tue Jul 21 19:12:33 2026 -0700 test(frontend): cover searchTestEntries (#6681) ### What changes were proposed in this PR? - Append a describe block to search-filter-parameters.spec.ts covering searchTestEntries. - Build DashboardEntry fixtures and assert keyword, date-range, owner, id, operator, projectId, and resource-type filtering including sequential combination. ### Any related issues, documentation, discussions? Closes: #6680 ### How was this PR tested? - Run: `cd frontend && node --max-old-space-size=8192 ./node_modules/nx/dist/bin/nx.js test gui --watch=false --include=src/app/dashboard/type/search-filter-parameters.spec.ts`, expect the suite passing. - Test-only change; no production code is modified. ### Was this PR authored or co-authored using generative AI tooling? Co-authored with Claude Opus 4.8 in compliance with ASF --- .../type/search-filter-parameters.spec.ts | 222 ++++++++++++++++++++- 1 file changed, 221 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/dashboard/type/search-filter-parameters.spec.ts b/frontend/src/app/dashboard/type/search-filter-parameters.spec.ts index b30ec3b571..fcf1250f93 100644 --- a/frontend/src/app/dashboard/type/search-filter-parameters.spec.ts +++ b/frontend/src/app/dashboard/type/search-filter-parameters.spec.ts @@ -17,8 +17,13 @@ * under the License. */ -import { SearchFilterParameters, toQueryStrings } from "./search-filter-parameters"; +import { searchTestEntries, SearchFilterParameters, toQueryStrings } from "./search-filter-parameters"; import { SortMethod } from "./sort-method"; +import { DashboardEntry } from "./dashboard-entry"; +import { DashboardWorkflow } from "./dashboard-workflow.interface"; +import { DashboardDataset } from "./dashboard-dataset.interface"; +import { ExecutionMode } from "../../common/type/workflow"; +import { OperatorPredicate } from "../../workspace/types/workflow-common.interface"; function makeEmptyFilter(): SearchFilterParameters { return { @@ -161,3 +166,218 @@ describe("toQueryStrings", () => { expect(toQueryStrings(["alpha"], filter)).toBe("query=alpha&owner=alice"); }); }); + +// Fixtures for searchTestEntries. Dates are built with local-time constructors so +// endOfDay() (which uses local getHours/setHours) is deterministic in every timezone. +interface WorkflowEntryOverrides { + name?: string; + wid?: number; + ownerName?: string; + creationTime?: number; + lastModifiedTime?: number; + operatorTypes?: string[]; + projectIDs?: number[]; +} + +function makeWorkflowEntry(overrides: WorkflowEntryOverrides = {}): DashboardEntry { + const operators = (overrides.operatorTypes ?? []).map( + (operatorType, i) => ({ operatorID: `op${i}`, operatorType }) as unknown as OperatorPredicate + ); + const workflow: DashboardWorkflow = { + isOwner: true, + ownerName: overrides.ownerName ?? "alice", + workflow: { + content: { + operators, + operatorPositions: {}, + links: [], + commentBoxes: [], + settings: { + dataTransferBatchSize: 400, + executionMode: ExecutionMode.PIPELINED, + }, + }, + name: overrides.name ?? "workflow", + description: "", + wid: overrides.wid ?? 1, + creationTime: overrides.creationTime, + lastModifiedTime: overrides.lastModifiedTime, + isPublished: 0, + readonly: false, + }, + projectIDs: overrides.projectIDs ?? [], + accessLevel: "WRITE", + ownerId: 10, + coverImage: null, + }; + return new DashboardEntry(workflow); +} + +function makeDatasetEntry(name = "dataset"): DashboardEntry { + const dataset: DashboardDataset = { + isOwner: false, + ownerEmail: "[email protected]", + dataset: { + did: 404, + ownerUid: 40, + name, + isPublic: true, + isDownloadable: true, + storagePath: "/datasets/404", + description: "", + creationTime: new Date(2024, 0, 15, 12).getTime(), + coverImage: "", + }, + accessPrivilege: "READ", + size: 100, + }; + return new DashboardEntry(dataset); +} + +describe("searchTestEntries", () => { + it("returns all entries untouched when nothing is constraining", () => { + const entries = [makeWorkflowEntry({ name: "alpha" }), makeWorkflowEntry({ name: "beta" })]; + expect(searchTestEntries([], makeEmptyFilter(), entries, null)).toEqual(entries); + }); + + it("keeps only entries whose name contains a keyword substring", () => { + const alpha = makeWorkflowEntry({ name: "alpha-report" }); + const beta = makeWorkflowEntry({ name: "beta-report" }); + const result = searchTestEntries(["alph"], makeEmptyFilter(), [alpha, beta], null); + expect(result).toEqual([alpha]); + }); + + it("matches when any of several keywords is a substring", () => { + const alpha = makeWorkflowEntry({ name: "alpha" }); + const beta = makeWorkflowEntry({ name: "beta" }); + const gamma = makeWorkflowEntry({ name: "gamma" }); + const result = searchTestEntries(["alp", "bet"], makeEmptyFilter(), [alpha, beta, gamma], null); + expect(result).toEqual([alpha, beta]); + }); + + it("filters by createDateStart, keeping entries created on or after the start", () => { + const before = makeWorkflowEntry({ name: "before", creationTime: new Date(2024, 0, 9, 12).getTime() }); + const onOrAfter = makeWorkflowEntry({ name: "after", creationTime: new Date(2024, 0, 11, 12).getTime() }); + const filter = makeEmptyFilter(); + filter.createDateStart = new Date(2024, 0, 10); + const result = searchTestEntries([], filter, [before, onOrAfter], null); + expect(result).toEqual([onOrAfter]); + }); + + it("filters by createDateEnd inclusively through the end of that day", () => { + // Late on the end day must still be included (endOfDay sets 23:59:59.999). + const lateSameDay = makeWorkflowEntry({ name: "same", creationTime: new Date(2024, 0, 10, 22, 0).getTime() }); + const nextDay = makeWorkflowEntry({ name: "next", creationTime: new Date(2024, 0, 11, 1, 0).getTime() }); + const filter = makeEmptyFilter(); + filter.createDateEnd = new Date(2024, 0, 10); + const result = searchTestEntries([], filter, [lateSameDay, nextDay], null); + expect(result).toEqual([lateSameDay]); + }); + + it("filters by modifiedDateStart and modifiedDateEnd against lastModifiedTime", () => { + const stale = makeWorkflowEntry({ name: "stale", lastModifiedTime: new Date(2024, 2, 1, 12).getTime() }); + const inRange = makeWorkflowEntry({ name: "inRange", lastModifiedTime: new Date(2024, 2, 15, 12).getTime() }); + const tooNew = makeWorkflowEntry({ name: "tooNew", lastModifiedTime: new Date(2024, 2, 20, 12).getTime() }); + const filter = makeEmptyFilter(); + filter.modifiedDateStart = new Date(2024, 2, 10); + filter.modifiedDateEnd = new Date(2024, 2, 16); + const result = searchTestEntries([], filter, [stale, inRange, tooNew], null); + expect(result).toEqual([inRange]); + }); + + it("filters by owner name, matching any listed owner", () => { + const alice = makeWorkflowEntry({ name: "a", ownerName: "alice" }); + const bob = makeWorkflowEntry({ name: "b", ownerName: "bob" }); + const carol = makeWorkflowEntry({ name: "c", ownerName: "carol" }); + const filter = makeEmptyFilter(); + filter.owners = ["alice", "carol"]; + const result = searchTestEntries([], filter, [alice, bob, carol], null); + expect(result).toEqual([alice, carol]); + }); + + it("filters by workflow id, comparing the wid as a string", () => { + const w7 = makeWorkflowEntry({ name: "w7", wid: 7 }); + const w8 = makeWorkflowEntry({ name: "w8", wid: 8 }); + const filter = makeEmptyFilter(); + filter.ids = ["7"]; + const result = searchTestEntries([], filter, [w7, w8], null); + expect(result).toEqual([w7]); + }); + + it("filters by operator type present in the workflow content", () => { + const hasCsv = makeWorkflowEntry({ name: "csv", operatorTypes: ["CSVFileScan", "Filter"] }); + const noCsv = makeWorkflowEntry({ name: "noCsv", operatorTypes: ["PythonUDFV2"] }); + const filter = makeEmptyFilter(); + filter.operators = ["CSVFileScan"]; + const result = searchTestEntries([], filter, [hasCsv, noCsv], null); + expect(result).toEqual([hasCsv]); + }); + + it("filters by projectId membership", () => { + const inProject = makeWorkflowEntry({ name: "in", projectIDs: [1, 2] }); + const notInProject = makeWorkflowEntry({ name: "out", projectIDs: [3] }); + const filter = makeEmptyFilter(); + filter.projectIds = [2]; + const result = searchTestEntries([], filter, [inProject, notInProject], null); + expect(result).toEqual([inProject]); + }); + + it("excludes non-workflow entries when a workflow-only filter is applied", () => { + // owners/ids/operators/projectIds all gate on e.type === "workflow". + const workflow = makeWorkflowEntry({ name: "wf", ownerName: "alice" }); + const dataset = makeDatasetEntry("ds"); + const filter = makeEmptyFilter(); + filter.owners = ["alice"]; + const result = searchTestEntries([], filter, [workflow, dataset], null); + expect(result).toEqual([workflow]); + }); + + it("filters by resource type when a type is given", () => { + const workflow = makeWorkflowEntry({ name: "wf" }); + const dataset = makeDatasetEntry("ds"); + const result = searchTestEntries([], makeEmptyFilter(), [workflow, dataset], "dataset"); + expect(result).toEqual([dataset]); + }); + + it("applies all filters sequentially, keeping only entries that satisfy every one", () => { + const match = makeWorkflowEntry({ + name: "alpha-pipeline", + wid: 7, + ownerName: "alice", + creationTime: new Date(2024, 0, 15, 12).getTime(), + lastModifiedTime: new Date(2024, 0, 16, 12).getTime(), + operatorTypes: ["CSVFileScan"], + projectIDs: [42], + }); + const wrongOwner = makeWorkflowEntry({ + name: "alpha-pipeline", + wid: 7, + ownerName: "bob", + creationTime: new Date(2024, 0, 15, 12).getTime(), + lastModifiedTime: new Date(2024, 0, 16, 12).getTime(), + operatorTypes: ["CSVFileScan"], + projectIDs: [42], + }); + const wrongName = makeWorkflowEntry({ + name: "beta-pipeline", + wid: 7, + ownerName: "alice", + creationTime: new Date(2024, 0, 15, 12).getTime(), + lastModifiedTime: new Date(2024, 0, 16, 12).getTime(), + operatorTypes: ["CSVFileScan"], + projectIDs: [42], + }); + const filter: SearchFilterParameters = { + createDateStart: new Date(2024, 0, 10), + createDateEnd: new Date(2024, 0, 20), + modifiedDateStart: new Date(2024, 0, 10), + modifiedDateEnd: new Date(2024, 0, 20), + owners: ["alice"], + ids: ["7"], + operators: ["CSVFileScan"], + projectIds: [42], + }; + const result = searchTestEntries(["alpha"], filter, [match, wrongOwner, wrongName], "workflow"); + expect(result).toEqual([match]); + }); +});
