aglinxinyuan commented on code in PR #6741:
URL: https://github.com/apache/texera/pull/6741#discussion_r3627633658
##########
frontend/src/app/workspace/component/property-editor/typecasting-display/type-casting-display.component.spec.ts:
##########
@@ -61,4 +65,195 @@ describe("TypecastingDisplayComponent", () => {
it("should create", () => {
expect(component).toBeTruthy();
});
+
+ afterEach(() => {
+ fixture.destroy();
+ vi.restoreAllMocks();
+ });
+
+ // Build an OperatorPredicate; when typeCastingUnits is supplied it is
placed under
+ // operatorProperties exactly as the TypeCasting operator descriptor stores
it.
+ const makeOperator = (
+ operatorID: string,
+ operatorType: string,
+ typeCastingUnits?: ReadonlyArray<{ attribute: string; resultType:
AttributeType }>
+ ): OperatorPredicate => ({
+ operatorID,
+ operatorType,
+ operatorVersion: "v1",
+ operatorProperties: typeCastingUnits ? { typeCastingUnits } : {},
+ inputPorts: [{ portID: "input-0" }],
+ outputPorts: [{ portID: "output-0" }],
+ showAdvanced: true,
+ isDisabled: false,
+ });
+
+ // The component reads/writes the same graph instance the injected
WorkflowActionService owns.
+ const getGraph = (): WorkflowGraph => (TestBed.inject(WorkflowActionService)
as any).texeraGraph as WorkflowGraph;
Review Comment:
Fixed — getGraph() now uses the public getTexeraGraph() accessor (casting to
WorkflowGraph only at the subject-push sites).
##########
frontend/src/app/workspace/component/property-editor/typecasting-display/type-casting-display.component.spec.ts:
##########
@@ -61,4 +65,195 @@ describe("TypecastingDisplayComponent", () => {
it("should create", () => {
expect(component).toBeTruthy();
});
+
+ afterEach(() => {
+ fixture.destroy();
+ vi.restoreAllMocks();
+ });
+
+ // Build an OperatorPredicate; when typeCastingUnits is supplied it is
placed under
+ // operatorProperties exactly as the TypeCasting operator descriptor stores
it.
+ const makeOperator = (
+ operatorID: string,
+ operatorType: string,
+ typeCastingUnits?: ReadonlyArray<{ attribute: string; resultType:
AttributeType }>
+ ): OperatorPredicate => ({
+ operatorID,
+ operatorType,
+ operatorVersion: "v1",
+ operatorProperties: typeCastingUnits ? { typeCastingUnits } : {},
+ inputPorts: [{ portID: "input-0" }],
+ outputPorts: [{ portID: "output-0" }],
+ showAdvanced: true,
+ isDisabled: false,
+ });
+
+ // The component reads/writes the same graph instance the injected
WorkflowActionService owns.
+ const getGraph = (): WorkflowGraph => (TestBed.inject(WorkflowActionService)
as any).texeraGraph as WorkflowGraph;
+
+ describe("ngOnChanges", () => {
+ it("hides the display and skips rerender when there is no
currentOperatorId", () => {
+ const rerenderSpy = vi.spyOn(component,
"rerender").mockImplementation(() => {});
+ component.currentOperatorId = undefined;
+ component.displayTypeCastingSchemaInformation = true;
+
+ component.ngOnChanges();
+
+ expect(component.displayTypeCastingSchemaInformation).toBe(false);
+ expect(rerenderSpy).not.toHaveBeenCalled();
+ });
+
+ it("hides the display and skips rerender when the operator is not a
TypeCasting operator", () => {
+ const graph = getGraph();
+ vi.spyOn(graph, "getOperator").mockReturnValue(makeOperator("op1",
"ScanSource"));
+ const rerenderSpy = vi.spyOn(component,
"rerender").mockImplementation(() => {});
+ component.currentOperatorId = "op1";
+ component.displayTypeCastingSchemaInformation = true;
+
+ component.ngOnChanges();
+
+ expect(component.displayTypeCastingSchemaInformation).toBe(false);
+ expect(rerenderSpy).not.toHaveBeenCalled();
+ });
+
+ it("shows the display and rerenders when the operator is a TypeCasting
operator", () => {
+ const graph = getGraph();
+ vi.spyOn(graph, "getOperator").mockReturnValue(
+ makeOperator("tc1", TYPE_CASTING_OPERATOR_TYPE, [{ attribute: "age",
resultType: "string" }])
+ );
+ const compiling = TestBed.inject(WorkflowCompilingService);
+ vi.spyOn(compiling, "getOperatorInputSchemaMap").mockReturnValue({
+ "port-0": [{ attributeName: "age", attributeType: "integer" }],
+ } as OperatorPortSchemaMap);
+ component.currentOperatorId = "tc1";
+
+ component.ngOnChanges();
+
+ expect(component.displayTypeCastingSchemaInformation).toBe(true);
+ expect(component.schemaToDisplay).toEqual([{ attributeName: "age",
attributeType: "string" }]);
+ });
+ });
+
+ describe("rerender", () => {
+ it("returns early and leaves schemaToDisplay untouched when
currentOperatorId is undefined", () => {
+ const compiling = TestBed.inject(WorkflowCompilingService);
+ const schemaSpy = vi.spyOn(compiling, "getOperatorInputSchemaMap");
+ const sentinel = [{ attributeName: "keep", attributeType: "string" as
AttributeType }];
+ component.currentOperatorId = undefined;
+ component.schemaToDisplay = sentinel;
+
+ component.rerender();
+
+ expect(component.schemaToDisplay).toBe(sentinel);
+ expect(schemaSpy).not.toHaveBeenCalled();
+ });
+
+ it("casts attributes listed in typeCastingUnits and passes the remaining
attributes through", () => {
+ const graph = getGraph();
+ vi.spyOn(graph, "getOperator").mockReturnValue(
+ makeOperator("tc1", TYPE_CASTING_OPERATOR_TYPE, [{ attribute: "age",
resultType: "string" }])
+ );
+ const compiling = TestBed.inject(WorkflowCompilingService);
+ // Two ports: port-0 carries the schema, port-1 is undefined and must be
skipped safely.
+ vi.spyOn(compiling, "getOperatorInputSchemaMap").mockReturnValue({
+ "port-0": [
+ { attributeName: "age", attributeType: "integer" },
+ { attributeName: "name", attributeType: "string" },
+ ],
+ "port-1": undefined,
+ } as OperatorPortSchemaMap);
+ component.currentOperatorId = "tc1";
+
+ component.rerender();
+
+ expect(component.schemaToDisplay).toEqual([
+ { attributeName: "age", attributeType: "string" },
+ { attributeName: "name", attributeType: "string" },
+ ]);
+ });
+
+ it("passes every attribute through unchanged when typeCastingUnits is
absent", () => {
+ const graph = getGraph();
+ vi.spyOn(graph, "getOperator").mockReturnValue(makeOperator("tc1",
TYPE_CASTING_OPERATOR_TYPE));
+ const compiling = TestBed.inject(WorkflowCompilingService);
+ vi.spyOn(compiling, "getOperatorInputSchemaMap").mockReturnValue({
+ "port-0": [{ attributeName: "x", attributeType: "integer" }],
+ } as OperatorPortSchemaMap);
+ component.currentOperatorId = "tc1";
+
+ component.rerender();
+
+ expect(component.schemaToDisplay).toEqual([{ attributeName: "x",
attributeType: "integer" }]);
+ });
+
+ it("produces an empty schemaToDisplay when there is no input schema", ()
=> {
+ const graph = getGraph();
+ vi.spyOn(graph, "getOperator").mockReturnValue(
+ makeOperator("tc1", TYPE_CASTING_OPERATOR_TYPE, [{ attribute: "age",
resultType: "string" }])
+ );
+ const compiling = TestBed.inject(WorkflowCompilingService);
+ vi.spyOn(compiling,
"getOperatorInputSchemaMap").mockReturnValue(undefined);
+ component.currentOperatorId = "tc1";
+ component.schemaToDisplay = [{ attributeName: "stale", attributeType:
"string" }];
+
+ component.rerender();
+
+ expect(component.schemaToDisplay).toEqual([]);
+ });
+ });
+
+ describe("stream handlers registered in ngOnInit", () => {
+ // The operator-property-change stream is a shared multicast: the real
ValidationWorkflowService
+ // also subscribes and calls validateOperator() on the emitted id. These
synthetic events reference
+ // operators that were never registered in metadata, so neutralize that
unrelated collaborator to
+ // keep the tests focused on the component's own handler (and free of
incidental console noise).
+ beforeEach(() => {
+ vi.spyOn(TestBed.inject(ValidationWorkflowService),
"validateOperator").mockReturnValue({ isValid: true });
+ });
+
+ it("rerenders when the matching TypeCasting operator's properties change",
() => {
+ const graph = getGraph();
+ const rerenderSpy = vi.spyOn(component,
"rerender").mockImplementation(() => {});
+ component.currentOperatorId = "tc1";
+
+ graph.operatorPropertyChangeSubject.next({
+ operator: makeOperator("tc1", TYPE_CASTING_OPERATOR_TYPE),
+ });
+
+ expect(rerenderSpy).toHaveBeenCalledTimes(1);
+ });
+
+ it("ignores property changes emitted for a different operator id", () => {
+ const graph = getGraph();
+ const rerenderSpy = vi.spyOn(component,
"rerender").mockImplementation(() => {});
+ component.currentOperatorId = "tc1";
+
+ graph.operatorPropertyChangeSubject.next({
+ operator: makeOperator("other", TYPE_CASTING_OPERATOR_TYPE),
+ });
+
+ expect(rerenderSpy).not.toHaveBeenCalled();
+ });
+
+ it("ignores property changes for the matching id but a non-TypeCasting
operator type", () => {
+ const graph = getGraph();
+ const rerenderSpy = vi.spyOn(component,
"rerender").mockImplementation(() => {});
+ component.currentOperatorId = "tc1";
+
+ graph.operatorPropertyChangeSubject.next({
+ operator: makeOperator("tc1", "ScanSource"),
+ });
+
+ expect(rerenderSpy).not.toHaveBeenCalled();
+ });
+
+ it("rerenders when the workflow compilation state changes", () => {
+ const compiling = TestBed.inject(WorkflowCompilingService);
+ const rerenderSpy = vi.spyOn(component,
"rerender").mockImplementation(() => {});
+
+ (compiling as
any).compilationStateInfoChangedStream.next(CompilationState.Succeeded);
+
Review Comment:
Fixed — the test now emits on a controlled rxjs Subject returned from a
mocked getCompilationStateInfoChangedStream(), instead of reaching into the
private stream field.
--
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: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]