-
Notifications
You must be signed in to change notification settings - Fork 25
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
Feat: set data action standalone #2513
Changes from all commits
8f1acb3
82ba9ed
f5611bf
e99ab48
8a681c6
2cab98b
7184921
afe1034
1a4f924
25b380a
16dca69
a75b533
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,200 @@ | ||
import { TestBed } from "@angular/core/testing"; | ||
|
||
import { HttpClientTestingModule } from "@angular/common/http/testing"; | ||
import { MockAppDataService } from "../data/app-data.service.spec"; | ||
import { AppDataService } from "../data/app-data.service"; | ||
|
||
import { IActionSetDataParams } from "./dynamic-data.actions"; | ||
import { DynamicDataService } from "./dynamic-data.service"; | ||
import ActionFactory from "./dynamic-data.actions"; | ||
import { firstValueFrom } from "rxjs"; | ||
import { FlowTypes } from "packages/data-models"; | ||
import { DeploymentService } from "../deployment/deployment.service"; | ||
import { MockDeploymentService } from "../deployment/deployment.service.spec"; | ||
import { TemplateActionRegistry } from "../../components/template/services/instance/template-action.registry"; | ||
|
||
const TEST_DATA_ROWS = [ | ||
{ id: "id_0", number: 0, string: "hello", _meta: "original" }, | ||
{ id: "id_1", number: 1, string: "hello", boolean: true, _meta: "original" }, | ||
]; | ||
|
||
/******************************************************************************** | ||
* Test Utilities | ||
*******************************************************************************/ | ||
|
||
/** Generate a rows to trigger set_data action with included params */ | ||
function getTestActionRow(params: IActionSetDataParams) { | ||
params._list_id = "test_flow"; | ||
const actionRow: FlowTypes.TemplateRowAction = { | ||
action_id: "set_data", | ||
trigger: "click", | ||
args: [], | ||
params, | ||
}; | ||
return actionRow; | ||
} | ||
|
||
/** | ||
* Trigger the set_data action with included params and return first update | ||
* to corresponding data_list | ||
* * */ | ||
async function triggerTestSetDataAction(service: DynamicDataService, params: IActionSetDataParams) { | ||
const actionRow = getTestActionRow(params); | ||
const actions = new ActionFactory(service); | ||
await actions.set_data(actionRow); | ||
const obs = await service.query$<any>("data_list", "test_flow"); | ||
const data = await firstValueFrom(obs); | ||
return data; | ||
} | ||
|
||
/******************************************************************************** | ||
* Tests | ||
* yarn ng test --include src/app/shared/services/dynamic-data/dynamic-data.actions.spec.ts | ||
*******************************************************************************/ | ||
describe("DynamicDataService Actions", () => { | ||
let service: DynamicDataService; | ||
let actions: ActionFactory; | ||
|
||
beforeEach(async () => { | ||
TestBed.configureTestingModule({ | ||
imports: [HttpClientTestingModule], | ||
providers: [ | ||
DynamicDataService, | ||
{ | ||
provide: AppDataService, | ||
useValue: new MockAppDataService({ | ||
data_list: { | ||
test_flow: { | ||
flow_name: "test_flow", | ||
flow_type: "data_list", | ||
// Make deep clone of data to avoid data overwrite issues | ||
rows: JSON.parse(JSON.stringify(TEST_DATA_ROWS)), | ||
}, | ||
}, | ||
}), | ||
}, | ||
{ | ||
provide: DeploymentService, | ||
useValue: new MockDeploymentService({ name: "test" }), | ||
}, | ||
{ | ||
provide: TemplateActionRegistry, | ||
useValue: { register: () => null }, | ||
}, | ||
], | ||
}); | ||
|
||
// HACK - polyfill not loaded for rxdb dev plugin so manually fill global before running tests | ||
window.global = window; | ||
|
||
service = TestBed.inject(DynamicDataService); | ||
await service.ready(); | ||
// Ensure any data previously persisted is cleared | ||
await service.resetFlow("data_list", "test_flow"); | ||
actions = new ActionFactory(service); | ||
}); | ||
|
||
/************************************************************* | ||
* Main Tests | ||
************************************************************/ | ||
it("set_data by _id", async () => { | ||
const params: IActionSetDataParams = { _id: "id_1", string: "updated string" }; | ||
const data = await triggerTestSetDataAction(service, params); | ||
expect(data[0].string).toEqual("hello"); | ||
expect(data[1].string).toEqual("updated string"); | ||
}); | ||
|
||
it("set_data by _index", async () => { | ||
const params: IActionSetDataParams = { _index: 1, string: "updated string" }; | ||
const data = await triggerTestSetDataAction(service, params); | ||
expect(data[0].string).toEqual("hello"); | ||
expect(data[1].string).toEqual("updated string"); | ||
}); | ||
|
||
it("set_data bulk", async () => { | ||
const params: IActionSetDataParams = { string: "updated string" }; | ||
const data = await triggerTestSetDataAction(service, params); | ||
expect(data[0].string).toEqual("updated string"); | ||
expect(data[1].string).toEqual("updated string"); | ||
}); | ||
|
||
it("set_data with item ref (single)", async () => { | ||
const params: IActionSetDataParams = { _id: "id_1", number: "@item.number + 100" }; | ||
const data = await triggerTestSetDataAction(service, params); | ||
expect(data[0].number).toEqual(0); | ||
expect(data[1].number).toEqual(101); | ||
}); | ||
|
||
it("set_data with item ref (bulk)", async () => { | ||
const params: IActionSetDataParams = { number: "@item.number + 100" }; | ||
const data = await triggerTestSetDataAction(service, params); | ||
expect(data[0].number).toEqual(100); | ||
expect(data[1].number).toEqual(101); | ||
}); | ||
|
||
it("set_data ignores updates for unchanged data", async () => { | ||
const params: IActionSetDataParams = { _list_id: "test_flow", number: 1 }; | ||
const updates = await actions["generateUpdateList"](params); | ||
expect(updates).toEqual([{ id: "id_0", number: 1 }]); | ||
}); | ||
|
||
it("set_data prevents update to metadata fields", async () => { | ||
const params: IActionSetDataParams = { _meta: "updated", string: "updated" }; | ||
const data = await triggerTestSetDataAction(service, params); | ||
expect(data[0].string).toEqual("updated"); | ||
expect(data[0]._meta).toEqual("original"); | ||
}); | ||
|
||
it("set_data ignores evaluation when _updates provided", async () => { | ||
const params: IActionSetDataParams = { _updates: [{ id: "id_0", number: "@item.number" }] }; | ||
const data = await triggerTestSetDataAction(service, params); | ||
// test case illustrative only of not parsing data (would have been parsed independently) | ||
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. So the point of this test case is just to demonstrate that using the 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. Yes, part of the idea with the parallel work on Although part of me is thinking that this inefficiency might actually be better just to simplify the |
||
expect(data[0].number).toEqual("@item.number"); | ||
expect(data[1].number).toEqual(1); | ||
}); | ||
|
||
it("reset_data action restores data to initial", async () => { | ||
const updatedData = await triggerTestSetDataAction(service, { string: "updated string" }); | ||
expect(updatedData[0].string).toEqual("updated string"); | ||
const resetActionBase = getTestActionRow({}); | ||
await actions.reset_data({ ...resetActionBase, action_id: "reset_data" }); | ||
const obs = await service.query$<any>("data_list", "test_flow"); | ||
const resetData = await firstValueFrom(obs); | ||
expect(resetData[0].string).toEqual("hello"); | ||
}); | ||
|
||
/************************************************************* | ||
* Misc | ||
************************************************************/ | ||
|
||
it("Coerces string params to correct data type", async () => { | ||
// NOTE - there is no specific code that casts variables, but RXDB will infer from schema | ||
const params: IActionSetDataParams = { number: "300" }; | ||
const data = await triggerTestSetDataAction(service, params); | ||
expect(data[0].number).toEqual(300); | ||
}); | ||
|
||
/************************************************************* | ||
* Quality Control | ||
************************************************************/ | ||
|
||
it("throws error if provided _id does not exist", async () => { | ||
const params = getTestActionRow({ | ||
_id: "missing_id", | ||
string: "sets an item correctly a given id", | ||
}); | ||
await expectAsync(actions.set_data(params)).toBeRejectedWithError( | ||
`[Update Fail] no doc exists\ndata_list: test_flow\n_id: missing_id` | ||
); | ||
}); | ||
|
||
it("throws error if provided _index does not exist", async () => { | ||
const params = getTestActionRow({ | ||
_index: 10, | ||
string: "sets an item correctly a given id", | ||
}); | ||
await expectAsync(actions.set_data(params)).toBeRejectedWithError( | ||
`[Update Fail] no doc exists\ndata_list: test_flow\n_index: 10` | ||
); | ||
}); | ||
}); |
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.
@esmeetewinkel noticed that this warning has been appearing, see #2528. I can't understand what's causing this as the comparison appears to be between two strings. It doesn't seem to cause any problems for functionality, but just flagging here
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.
Yeah the console log is wrong as up till this point we've checked:
so what we are left with are non-equivalent but same type (strings, numbers etc.), or additional primative types not already checked (e.g. date objects, bigints etc.).
So if we wanted we could add a type check for string, number and boolean types and confidently return
false
, and then add a log just to catch other types; but for now I've just dropped the log and added a note on the function limitation. I don't see this being of practical consequence as there's currently no way to construct other primitive types from the code.