Refactor client tests to use await for mutations

According to Vue Test Utils documentation:
'A nice rule to follow is to always `await` on mutations like `trigger` or `setProps`. If your code relies on something async, like calling axios, add an `await` to the `flushPromises` call as well.'
This commit is contained in:
davelopez
2022-02-18 12:38:48 +01:00
parent f13faf8bba
commit 86d6dbdc67
21 changed files with 67 additions and 130 deletions
@@ -45,12 +45,11 @@ describe("PairedListCollectionCreator", () => {
});
await wrapper.vm.$nextTick();
//change filter to .1.fastq/.2.fastq
wrapper.find("div.forward-unpaired-filter > div.input-group-append > button").trigger("click");
wrapper
await wrapper.find("div.forward-unpaired-filter > div.input-group-append > button").trigger("click");
await wrapper
.findAll("div.dropdown-menu > a.dropdown-item")
.wrappers.find((e) => e.text() == ".1.fastq")
.trigger("click");
await wrapper.vm.$nextTick();
//assert forward filter
const forwardFilter = wrapper.find("div.forward-unpaired-filter > input").element.value;
expect(forwardFilter).toBe(".1.fastq");
@@ -58,8 +57,7 @@ describe("PairedListCollectionCreator", () => {
const reverseFilter = wrapper.find("div.reverse-unpaired-filter > input").element.value;
expect(reverseFilter).toBe(".2.fastq");
// click Autopair
wrapper.find("a.autopair-link").trigger("click");
await wrapper.vm.$nextTick();
await wrapper.find("a.autopair-link").trigger("click");
//assert pair-name longer name
const pairname = wrapper.find("span.pair-name");
expect(pairname.text()).toBe("DP134_1_FS_PSII_FSB_42C_A10");
@@ -83,12 +81,11 @@ describe("PairedListCollectionCreator", () => {
});
await wrapper.vm.$nextTick();
//change filter to .1.fastq/.2.fastq
wrapper.find("div.forward-unpaired-filter > div.input-group-append > button").trigger("click");
wrapper
await wrapper.find("div.forward-unpaired-filter > div.input-group-append > button").trigger("click");
await wrapper
.findAll("div.dropdown-menu > a.dropdown-item")
.wrappers.find((e) => e.text() == ".1.fastq")
.trigger("click");
await wrapper.vm.$nextTick();
//assert forward filter
const forwardFilter = wrapper.find("div.forward-unpaired-filter > input").element.value;
expect(forwardFilter).toBe(".1.fastq");
@@ -96,8 +93,7 @@ describe("PairedListCollectionCreator", () => {
const reverseFilter = wrapper.find("div.reverse-unpaired-filter > input").element.value;
expect(reverseFilter).toBe(".2.fastq");
// click Autopair
wrapper.find("a.autopair-link").trigger("click");
await wrapper.vm.$nextTick();
await wrapper.find("a.autopair-link").trigger("click");
//assert pair-name longer name
const pairname = wrapper.find("span.pair-name");
expect(pairname.text()).toBe("UII_moo_1");
@@ -121,10 +117,8 @@ describe("PairedListCollectionCreator", () => {
});
await wrapper.vm.$nextTick();
//change filter to _R1/_R2
wrapper.find("div.forward-unpaired-filter > input").setValue("_R1");
await wrapper.vm.$nextTick();
wrapper.find("div.reverse-unpaired-filter > input").setValue("_R2");
await wrapper.vm.$nextTick();
await wrapper.find("div.forward-unpaired-filter > input").setValue("_R1");
await wrapper.find("div.reverse-unpaired-filter > input").setValue("_R2");
//assert forward filter
const forwardFilter = wrapper.find("div.forward-unpaired-filter > input").element.value;
expect(forwardFilter).toBe("_R1");
@@ -132,8 +126,7 @@ describe("PairedListCollectionCreator", () => {
const reverseFilter = wrapper.find("div.reverse-unpaired-filter > input").element.value;
expect(reverseFilter).toBe("_R2");
// click Autopair
wrapper.find("a.autopair-link").trigger("click");
await wrapper.vm.$nextTick();
await wrapper.find("a.autopair-link").trigger("click");
//assert all pairs matched
expect(wrapper.findAll("li.dataset unpaired").length == 0).toBeTruthy();
});
@@ -1,4 +1,3 @@
import Vue from "vue";
import axios from "axios";
import { mount } from "@vue/test-utils";
import { getLocalVue } from "jest/helpers";
@@ -9,8 +8,8 @@ import flushPromises from "flush-promises";
const localVue = getLocalVue();
function buildWrapper(conversion_disable = false) {
return mount(DatasetAttributes, {
async function buildWrapper(conversion_disable = false) {
const wrapper = mount(DatasetAttributes, {
propsData: {
datasetId: "dataset_id",
},
@@ -29,14 +28,15 @@ function buildWrapper(conversion_disable = false) {
FormElement: false,
},
});
await flushPromises();
return wrapper;
}
describe("DatasetAttributes", () => {
it("check rendering", async () => {
const axiosMock = new MockAdapter(axios);
axiosMock.onPut(`/dataset/set_edit`).reply(200, { message: "success", status: "success" });
const wrapper = buildWrapper();
await Vue.nextTick();
const wrapper = await buildWrapper();
expect(wrapper.findAll("button").length).toBe(6);
expect(wrapper.findAll("#attribute_text").length).toBe(1);
expect(wrapper.findAll("#conversion_text").length).toBe(1);
@@ -50,8 +50,7 @@ describe("DatasetAttributes", () => {
});
it("check rendering without conversion option", async () => {
const wrapper = buildWrapper(true);
await Vue.nextTick();
const wrapper = await buildWrapper(true);
expect(wrapper.findAll("button").length).toBe(5);
expect(wrapper.findAll("#attribute_text").length).toBe(1);
expect(wrapper.findAll("#conversion_text").length).toBe(0);
@@ -1,7 +1,6 @@
import { mount } from "@vue/test-utils";
import { getLocalVue } from "jest/helpers";
import FormBoolean from "./FormBoolean";
import Vue from "vue";
const localVue = getLocalVue();
@@ -20,12 +19,11 @@ describe("FormBoolean", () => {
it("check initial value and value change", async () => {
const input = wrapper.find("input");
expect(wrapper.vm.currentValue).toBe(false);
wrapper.setProps({ value: true });
await Vue.nextTick();
await wrapper.setProps({ value: true });
expect(wrapper.vm.currentValue).toBe(true);
input.trigger("click");
await input.trigger("click");
expect(input.element.checked).toBe(false);
input.trigger("click");
await input.trigger("click");
expect(input.element.checked).toBe(true);
expect(wrapper.emitted().input[0][0]).toBe(true);
});
@@ -52,9 +52,8 @@ describe("FormInput", () => {
const props = { value: 50, type: "float", min: 10, max: 100 };
const wrapper = await mountFormNumber(props);
const input = await getInput(wrapper);
input.setValue(number);
input.trigger("change");
await flushPromises();
await input.setValue(number);
await input.trigger("change");
const alert = await getAlert(wrapper);
expect(alert.exists()).toBeTruthy();
expect(alert.text().includes(`${number} is out`)).toBeTruthy();
@@ -1,6 +1,5 @@
import { mount } from "@vue/test-utils";
import { getLocalVue } from "jest/helpers";
import Vue from "vue";
import FormElement from "./FormElement";
const localVue = getLocalVue();
@@ -46,19 +45,17 @@ describe("FormElement", () => {
expect(wrapper.find(".ui-form-title-text").text()).toEqual("title_text");
expect(wrapper.findAll("span[title='Disable']").length).toEqual(1);
expect(wrapper.emitted().input[0][0]).toEqual("initial_value");
wrapper.find(".ui-form-collapsible-icon").trigger("click");
await wrapper.find(".ui-form-collapsible-icon").trigger("click");
expect(wrapper.emitted().input[1][0]).toEqual("collapsible_value");
expect(wrapper.emitted().input[1][1]).toEqual("input");
await Vue.nextTick();
await wrapper.setProps({
collapsedEnableText: "Enable Collapsible",
collapsedDisableText: "Disable Collapsible",
});
expect(wrapper.findAll("span[title='Enable Collapsible']").length).toEqual(1);
expect(wrapper.findAll("span[title='Disable Collapsible']").length).toEqual(0);
wrapper.find(".ui-form-collapsible-icon").trigger("click");
await wrapper.find(".ui-form-collapsible-icon").trigger("click");
expect(wrapper.emitted().input[2][0]).toEqual("default_value");
await Vue.nextTick();
expect(wrapper.findAll("span[title='Disable Collapsible']").length).toEqual(1);
expect(wrapper.findAll("span[title='Enable Collapsible']").length).toEqual(0);
});
@@ -1,7 +1,6 @@
import DatasetCollectionUIWrapper from "./DatasetCollectionUIWrapper";
import DscUI from "components/History/ContentItem/DatasetCollection/DscUI";
import { shallowMount } from "@vue/test-utils";
import flushPromises from "flush-promises";
import datasetCollectionRaw from "components/providers/test/json/DatasetCollection.json";
import datasetCollectionContent from "components/providers/test/json/DatasetCollection.nested.json";
@@ -54,8 +53,7 @@ describe("DatasetUIWrapper.vue with Dataset", () => {
expect(wrapper.vm.expand).toBeTruthy();
});
it("build dsc from collection content", async () => {
wrapper.setProps({ item: datasetCollectionContent });
await flushPromises();
await wrapper.setProps({ item: datasetCollectionContent });
expect(wrapper.vm.datasetCollection.name).toBe(datasetCollectionContent.element_identifier);
});
});
@@ -93,11 +93,9 @@ describe("Masthead.vue", () => {
it("should disable brand when displayGalaxyBrand is true", async () => {
expect(wrapper.find(".navbar-brand-title").text()).toBe("Galaxy");
wrapper.setProps({ brand: "Foo " });
await localVue.nextTick();
await wrapper.setProps({ brand: "Foo " });
expect(wrapper.find(".navbar-brand-title").text()).toBe("Galaxy Foo");
wrapper.setProps({ displayGalaxyBrand: false });
await localVue.nextTick();
await wrapper.setProps({ displayGalaxyBrand: false });
expect(wrapper.find(".navbar-brand-title").text()).toBe("Foo");
});
@@ -137,8 +135,7 @@ describe("Masthead.vue", () => {
it("should display scratchbook button", async () => {
expect(wrapper.find("#enable-scratchbook a span").classes("fa-th")).toBe(true);
expect(scratchbook.active).toBe(false);
wrapper.find("#enable-scratchbook a").trigger("click");
await localVue.nextTick();
await wrapper.find("#enable-scratchbook a").trigger("click");
expect(scratchbook.active).toBe(true);
});
@@ -40,14 +40,12 @@ describe("ToolSection", () => {
expect(wrapper.vm.opened).toBe(false);
const $sectionName = wrapper.find(".name");
expect($sectionName.text()).toBe("tool_section");
$sectionName.trigger("click");
await wrapper.vm.$nextTick();
await $sectionName.trigger("click");
const $names = wrapper.findAll(".name");
expect($names.at(1).text()).toBe("name");
const $label = wrapper.find(".title-link");
expect($label.text()).toBe("tool_section");
$sectionName.trigger("click");
await wrapper.vm.$nextTick();
await $sectionName.trigger("click");
expect(wrapper.findAll(".name").length).toBe(1);
});
@@ -71,26 +69,19 @@ describe("ToolSection", () => {
});
expect(wrapper.vm.opened).toBe(true);
const $sectionName = wrapper.find(".name");
$sectionName.trigger("click");
await wrapper.vm.$nextTick();
await $sectionName.trigger("click");
expect(wrapper.vm.opened).toBe(false);
wrapper.setProps({ queryFilter: "" });
await wrapper.vm.$nextTick();
await wrapper.setProps({ queryFilter: "" });
expect(wrapper.vm.opened).toBe(false);
wrapper.setProps({ queryFilter: "test" });
await wrapper.vm.$nextTick();
await wrapper.setProps({ queryFilter: "test" });
expect(wrapper.vm.opened).toBe(true);
wrapper.setProps({ disableFilter: true });
await wrapper.vm.$nextTick();
await wrapper.setProps({ disableFilter: true });
expect(wrapper.vm.opened).toBe(true);
wrapper.setProps({ queryFilter: "" });
await wrapper.vm.$nextTick();
await wrapper.setProps({ queryFilter: "" });
expect(wrapper.vm.opened).toBe(false);
$sectionName.trigger("click");
await wrapper.vm.$nextTick();
await $sectionName.trigger("click");
expect(wrapper.vm.opened).toBe(true);
wrapper.setProps({ queryFilter: "test" });
await wrapper.vm.$nextTick();
await wrapper.setProps({ queryFilter: "test" });
expect(wrapper.vm.opened).toBe(false);
});
});
@@ -46,11 +46,10 @@ describe("SavedRulesSelector", () => {
},
],
};
wrapper.setProps({
await wrapper.setProps({
user: "test_user",
savedRules: [testRules],
});
await wrapper.vm.$nextTick();
const sessions = wrapper.findAll("div.dropdown-menu > a.saved-rule-item");
expect(sessions.length > 0).toBeTruthy();
sessions.wrappers[0].trigger("click");
@@ -36,8 +36,7 @@ describe("SelectionDialog.vue", () => {
expect(wrapper.get(".fa-spinner"));
expect(wrapper.get(".fa-spinner").text()).toBe("");
//expect(wrapper.get("#tree-options")).toThrow();
wrapper.setProps({ optionsShow: true });
await localVue.nextTick();
await wrapper.setProps({ optionsShow: true });
expect(() => wrapper.get(".fa-spinner")).toThrow();
expect(wrapper.get("#tree-options"));
});
@@ -50,10 +49,8 @@ describe("SelectionDialog.vue", () => {
it("hideModal called on click cancel", async () => {
expect(calledHide).toBe(false);
expect(wrapper.get(".selection-dialog-modal-cancel"));
wrapper.setProps({ optionsShow: true });
await localVue.nextTick();
wrapper.find(".selection-dialog-modal-cancel").trigger("click");
await localVue.nextTick();
await wrapper.setProps({ optionsShow: true });
await wrapper.find(".selection-dialog-modal-cancel").trigger("click");
expect(calledHide).toBe(true);
});
});
@@ -1,7 +1,6 @@
import { mount, createLocalVue } from "@vue/test-utils";
import StatelessTags from "./StatelessTags";
import _l from "utils/localization";
import flushPromises from "flush-promises";
describe("Tags/StatelessTags.vue", () => {
const localVue = createLocalVue();
@@ -13,11 +12,10 @@ describe("Tags/StatelessTags.vue", () => {
beforeEach(async () => {
wrapper = mount(StatelessTags, { localVue });
wrapper.setProps({
await wrapper.setProps({
value: testTags,
});
emitted = wrapper.emitted();
await wrapper.vm.$nextTick();
});
it("should render a div for each tag", () => {
@@ -47,8 +45,7 @@ describe("Tags/StatelessTags.vue", () => {
it("should change internal model representation when new tag list assigned", async () => {
expect(wrapper.vm.tagModels.length).toBe(3);
const newTags = ["floob", "clown", "hoohah", "doodoo"];
wrapper.setProps({ value: newTags });
await flushPromises();
await wrapper.setProps({ value: newTags });
expect(wrapper.vm.tagModels.length).toBe(newTags.length);
});
});
+1 -8
View File
@@ -85,14 +85,7 @@ describe("Tags/Tags.vue", () => {
const newTags = ["asdfadsadf", "gfhjfghjf"];
testStore.dispatch("updateTags", { key: storeKey, tags: newTags });
// TODO: figure out how to make the computed observableTags
// prop update when the store does. This works in the real code,
// but does not update in this test environment. The following
// brute force mechanism of changing a different dependency works
// and effectively recalculates the computed value, but it should
// not be necessary
wrapper.setProps({ storeKey: "thisshouldbeunnecessary" });
wrapper.setProps({ storeKey });
await wrapper.setProps({ storeKey });
const observed = wrapper.vm.observedTags;
expect(observed.length).toBe(newTags.length);
@@ -48,7 +48,7 @@ describe("ToolFooter", () => {
const referenceA = wrapper.find(".formatted-reference .csl-entry");
expect(referenceA.attributes()["data-csl-entry-id"]).toBe("entry_a");
expect(referenceA.text()).toContain("1111");
wrapper.setProps({ id: "tool_b" });
await wrapper.setProps({ id: "tool_b" });
await flushPromises();
const referenceB = wrapper.find(".formatted-reference .csl-entry");
expect(referenceB.attributes()["data-csl-entry-id"]).toBe("entry_b");
@@ -59,8 +59,7 @@ describe("RepositoryTools", () => {
expect($third.find("td:first-child").text()).toBe("Show more");
expect($third.find("td:last-child").text()).toBe("");
const $link = wrapper.find("a");
$link.trigger("click");
await wrapper.vm.$nextTick();
await $link.trigger("click");
const $elExpanded = wrapper.findAll("tr");
expect($elExpanded.length).toBe(4);
@@ -71,8 +70,7 @@ describe("RepositoryTools", () => {
expect($forthExpanded.find("td:first-child").text()).toBe("Show less");
expect($forthExpanded.find("td:last-child").text()).toBe("");
const $linkExpanded = wrapper.find("a");
$linkExpanded.trigger("click");
await wrapper.vm.$nextTick();
await $linkExpanded.trigger("click");
const $elCollapsed = wrapper.findAll("tr");
expect($elCollapsed.length).toBe(3);
@@ -31,16 +31,14 @@ describe("Collection.vue", () => {
const { wrapper, localVue } = mountWithApp(Collection);
expect(wrapper.vm.showHelper).toBe(true);
await localVue.nextTick();
wrapper.find("#btn-new").trigger("click");
await localVue.nextTick();
await wrapper.find("#btn-new").trigger("click");
expect(wrapper.vm.showHelper).toBe(false);
expect(wrapper.vm.counterAnnounce).toBe(1);
expect(wrapper.find("#btn-reset").classes()).toEqual(expect.not.arrayContaining(["disabled"]));
expect(wrapper.find("#btn-start").classes()).toEqual(expect.not.arrayContaining(["disabled"]));
wrapper.find("#btn-reset").trigger("click");
await localVue.nextTick();
await wrapper.find("#btn-reset").trigger("click");
expect(wrapper.vm.showHelper).toBe(true);
});
@@ -48,13 +46,10 @@ describe("Collection.vue", () => {
const { wrapper, localVue } = mountWithApp(Collection, {}, { lazyLoadMax: 2 });
expect(wrapper.findAll(".ui-limitloader").length).toBe(1);
await localVue.nextTick();
wrapper.find("#btn-new").trigger("click");
await localVue.nextTick();
wrapper.find("#btn-new").trigger("click");
await localVue.nextTick();
await wrapper.find("#btn-new").trigger("click");
await wrapper.find("#btn-new").trigger("click");
expect(wrapper.findAll("table tbody tr").length).toBe(2);
wrapper.find("#btn-new").trigger("click");
await localVue.nextTick();
await wrapper.find("#btn-new").trigger("click");
expect(wrapper.findAll("table tbody tr").length).toBe(2);
expect(wrapper.find(".ui-limitloader").text()).toEqual(expect.stringContaining("only the first 2 entries"));
});
+4 -7
View File
@@ -20,10 +20,9 @@ describe("Default.vue", () => {
});
it("does render FTP is site set", async () => {
const { wrapper, localVue } = mountWithApp(Default);
const { wrapper } = mountWithApp(Default);
expect(wrapper.find("#btn-ftp").element).toBeVisible();
wrapper.find("#btn-ftp").trigger("click");
await localVue.nextTick();
await wrapper.find("#btn-ftp").trigger("click");
// TODO: test popover appears... not sure best way to do this...
});
@@ -40,12 +39,10 @@ describe("Default.vue", () => {
const { wrapper, localVue } = mountWithApp(Default);
expect(wrapper.vm.showHelper).toBe(true);
await localVue.nextTick();
wrapper.find("#btn-new").trigger("click");
await localVue.nextTick();
await wrapper.find("#btn-new").trigger("click");
expect(wrapper.vm.showHelper).toBe(false);
expect(wrapper.vm.counterAnnounce).toBe(1);
wrapper.find("#btn-reset").trigger("click");
await localVue.nextTick();
await wrapper.find("#btn-reset").trigger("click");
expect(wrapper.vm.showHelper).toBe(true);
});
@@ -32,8 +32,7 @@ describe("Attributes", () => {
const name = wrapper.find("#workflow-name");
expect(name.element.value).toBe(TEST_NAME);
wrapper.setProps({ name: "new_workflow_name" });
await localVue.nextTick();
await wrapper.setProps({ name: "new_workflow_name" });
expect(name.element.value).toBe("new_workflow_name");
const parameters = wrapper.findAll(".list-group-item");
expect(parameters.length).toBe(2);
@@ -26,8 +26,7 @@ describe("Node", () => {
expect(icon.at(2).classes()).toEqual(expect.arrayContaining(["fa-wrench"]));
const toolLinks = wrapper.findAll("i");
expect(toolLinks.length).toBe(3);
wrapper.setProps({ type: "subworkflow" });
await flushPromises();
await wrapper.setProps({ type: "subworkflow" });
expect(icon.at(2).classes()).toEqual(expect.arrayContaining(["fa-sitemap"]));
const subworkflowLinks = wrapper.findAll("i");
expect(subworkflowLinks.length).toBe(2);
@@ -24,10 +24,9 @@ describe("RefactorConfirmationModal.vue", () => {
});
it("should not attempt a dry run refactor is there are no actions", async () => {
wrapper.setProps({
await wrapper.setProps({
refactorActions: [],
});
await flushPromises();
expect(refactor.mock.calls.length).toBe(0);
});
@@ -37,7 +36,7 @@ describe("RefactorConfirmationModal.vue", () => {
error("foo");
})
);
wrapper.setProps({
await wrapper.setProps({
refactorActions: [{ action_type: TEST_ACTION_TYPE }],
});
await flushPromises();
@@ -57,7 +56,7 @@ describe("RefactorConfirmationModal.vue", () => {
});
})
);
wrapper.setProps({
await wrapper.setProps({
refactorActions: [{ action_type: TEST_ACTION_TYPE }],
});
await flushPromises();
@@ -91,10 +90,9 @@ describe("RefactorConfirmationModal.vue", () => {
});
})
);
wrapper.setProps({
await wrapper.setProps({
refactorActions: [{ action_type: TEST_ACTION_TYPE }],
});
await flushPromises();
expect(wrapper.emitted().onWorkflowError).toBeFalsy();
// called with dry run...
@@ -15,14 +15,11 @@ describe("ZoomControl", () => {
});
const buttons = wrapper.findAll("button");
expect(buttons.length).toBe(3);
buttons.at(0).trigger("click");
await localVue.nextTick();
await buttons.at(0).trigger("click");
expect(wrapper.emitted().onZoom[0][0]).toBe(9);
buttons.at(1).trigger("click");
await localVue.nextTick();
await buttons.at(1).trigger("click");
expect(wrapper.emitted().onZoom[1][0]).toBe(10);
buttons.at(2).trigger("click");
await localVue.nextTick();
await buttons.at(2).trigger("click");
expect(wrapper.emitted().onZoom[2][0]).toBe(11);
});
});
@@ -35,13 +35,11 @@ describe("DatasetUIWrapper.vue with Dataset", () => {
expect(wrapper.vm.toggledItems["1"]).toBe(undefined);
expect(wrapper.find(".expanded").exists()).toBeFalsy();
// expand
wrapper.find("tbody").find("tbody").find("tr").trigger("click");
await localVue.nextTick();
await wrapper.find("tbody").find("tbody").find("tr").trigger("click");
expect(wrapper.vm.toggledItems["1"]).toBeTruthy();
expect(wrapper.find(".expanded").exists()).toBeTruthy();
wrapper.find("tbody").find("tbody").find("tr").trigger("click");
await wrapper.find("tbody").find("tbody").find("tr").trigger("click");
// close again
await localVue.nextTick();
expect(wrapper.vm.toggledItems["1"]).toBeFalsy();
expect(wrapper.find(".expanded").exists()).toBeFalsy();
});
@@ -50,16 +48,14 @@ describe("DatasetUIWrapper.vue with Dataset", () => {
expect(wrapper.vm.toggledItems["1"]).toBe(undefined);
expect(wrapper.find(".expanded").exists()).toBeFalsy();
// expand
wrapper.find("tbody").find("tbody").find("tr").trigger("click");
await localVue.nextTick();
await wrapper.find("tbody").find("tbody").find("tr").trigger("click");
expect(wrapper.vm.toggledItems["1"]).toBeTruthy();
expect(wrapper.find(".expanded").exists()).toBeTruthy();
// 2 collapsed rows, plus 1 expanded row
expect(wrapper.find("tbody").findAll("tr").length).toBe(3);
// update data
const additionalJob = { ...jobs[0], id: 3 };
wrapper.setProps({ jobs: [...jobs, additionalJob] });
await localVue.nextTick();
await wrapper.setProps({ jobs: [...jobs, additionalJob] });
// verify new data is displayed
expect(wrapper.find("tbody").findAll("tr").length).toBe(4);
// verify first row is still expanded