Skip to content
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

History refactors #15686

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
544e264
convert historyItemsStore to typescript
ElectronicBlueberry Feb 13, 2023
6bdf214
remove duplicate mappings
ElectronicBlueberry Feb 13, 2023
cbed25e
fix pinia binding not reactive
ElectronicBlueberry Feb 13, 2023
e20f4c8
clean up pinia bindings
ElectronicBlueberry Feb 13, 2023
3b0b873
fix types
ElectronicBlueberry Mar 1, 2023
53104b5
remove margin
ElectronicBlueberry Mar 1, 2023
ba2b31b
improve estimated height
ElectronicBlueberry Mar 1, 2023
3218a0f
replace test with typescript
ElectronicBlueberry Mar 1, 2023
c436db2
refactor entry point store
ElectronicBlueberry Mar 1, 2023
2356e67
refactor content item
ElectronicBlueberry Mar 1, 2023
3b24e59
refactor content options
ElectronicBlueberry Mar 1, 2023
1d0b55c
refactor collection description
ElectronicBlueberry Mar 2, 2023
9e4d9c7
remove unused legacy component
ElectronicBlueberry Mar 2, 2023
6ba8fec
add todo note
ElectronicBlueberry Mar 2, 2023
bace1a2
refactor collection progress
ElectronicBlueberry Mar 2, 2023
e0adeb6
refactor dataset details
ElectronicBlueberry Mar 2, 2023
8d23e64
remove element selector
ElectronicBlueberry Mar 2, 2023
eaee005
fix loading state
ElectronicBlueberry Mar 2, 2023
82c57df
move lodash types to client package
ElectronicBlueberry Mar 2, 2023
7937e0b
fix pluralized item text
ElectronicBlueberry Mar 2, 2023
2a058ee
expose poll timeout
ElectronicBlueberry Mar 2, 2023
7758602
add missing event typedefs
ElectronicBlueberry Mar 2, 2023
2099c83
fix missing space
ElectronicBlueberry Mar 2, 2023
a797b7a
fix getter functions
ElectronicBlueberry Mar 2, 2023
ebbbdf7
add missing import
ElectronicBlueberry Mar 2, 2023
05df71c
fix test
ElectronicBlueberry Mar 2, 2023
c9540af
make tests granular
ElectronicBlueberry Mar 2, 2023
dafcdc6
fix generic element test
ElectronicBlueberry Mar 3, 2023
8bcd852
fix job parameters test
ElectronicBlueberry Mar 3, 2023
395886c
fix fetchOnce
ElectronicBlueberry Mar 24, 2023
110c13d
remove noFetch from useCurrentUser
ElectronicBlueberry Mar 24, 2023
7ed3e5a
refactor HistoryCounter
ElectronicBlueberry Mar 24, 2023
743e689
fix: interval potentially undefined
ElectronicBlueberry Mar 24, 2023
a02fc94
move library add
ElectronicBlueberry Mar 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
"@types/jquery": "^3.5.16",
"@types/markdown-it": "^12.2.3",
"@types/underscore": "^1.11.4",
"@types/lodash.isequal": "^4.5.6",
"@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.51.0",
"@vue/test-utils": "^1.3.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,74 +1,57 @@
<script setup lang="ts">
import CollectionProgress from "./CollectionProgress.vue";
import { computed } from "vue";
import type { JobStateSummary } from "./JobStateSummary";

const props = withDefaults(
defineProps<{
jobStateSummary: JobStateSummary;
collectionType: string;
elementCount?: number;
elementsDatatypes?: string[];
}>(),
{
elementsDatatypes: () => [],
}
);

const labels = new Map([
["list", "list"],
["list:paired", "list"],
["list:list", "list"],
["paired", "pair"],
]);

const collectionLabel = computed(() => labels.get(props.collectionType) ?? "nested list");
const hasSingleElement = computed(() => props.elementCount === 1);
const isHomogeneous = computed(() => props.elementsDatatypes.length === 1);
const homogeneousDatatype = computed(() => (isHomogeneous.value ? " " + props.elementsDatatypes[0] : ""));
const pluralizedItem = computed(() => {
if (props.collectionType === "list:list") {
return pluralize("list");
} else if (props.collectionType === "list:paired") {
return pluralize("pair");
} else if (!labels.has(props.collectionType)) {
return pluralize("dataset collection");
} else {
return pluralize("dataset");
}
});

function pluralize(word: string): string {
return hasSingleElement.value ? word : `${word}s`;
}
</script>

<template>
<div>
<span class="description mt-1 mb-1">
a {{ collectionLabel }} with {{ elementCount }}<b>{{ homogeneousDatatype }}</b> {{ pluralizedItem }}
a {{ collectionLabel }} with {{ props.elementCount }}<b>{{ homogeneousDatatype }}</b> {{ pluralizedItem }}
</span>
<CollectionProgress v-if="jobStateSummary.size != 0" :summary="jobStateSummary" />
<CollectionProgress v-if="props.jobStateSummary.size !== 0" :summary="props.jobStateSummary" />
</div>
</template>

<script>
import CollectionProgress from "./CollectionProgress";
import { JobStateSummary } from "./JobStateSummary";

export default {
components: { CollectionProgress },
props: {
jobStateSummary: { type: JobStateSummary, required: true },
collectionType: { type: String, required: true },
elementCount: { type: Number, required: false, default: undefined },
elementsDatatypes: { type: Array, required: false, default: () => [] },
},
data() {
return {
labels: {
list: "list",
"list:paired": "list",
"list:list": "list",
paired: "pair",
},
};
},
computed: {
/**@return {String} */
collectionLabel() {
return this.labels[this.collectionType] || "nested list";
},
/**@return {Boolean} */
hasSingleElement() {
return this.elementCount === 1;
},
/**@return {Boolean} */
isHomogeneous() {
return this.elementsDatatypes.length === 1;
},
/**@return {String} */
homogeneousDatatype() {
return this.isHomogeneous ? ` ${this.elementsDatatypes[0]}` : "";
},
/**@return {String} */
pluralizedItem() {
if (this.collectionType === "list:list") {
return this.pluralize("list");
}
if (this.collectionType === "list:paired") {
return this.pluralize("pair");
}
if (!Object.keys(this.labels).includes(this.collectionType)) {
//Any other kind of nested collection
return this.pluralize("dataset collection");
}
return this.pluralize("dataset");
},
},
methods: {
pluralize(word) {
return this.hasSingleElement ? word : `${word}s`;
},
},
};
</script>

<style lang="scss" scoped>
@import "scss/theme/blue.scss";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
<!-- Job state progress bar for a collection. There's another similar component
at components/JobStates/CollectionJobStates but it relies on the backbone data
model, so probably has to go eventually.-->
<script setup lang="ts">
import { BProgress, BProgressBar } from "bootstrap-vue";

const props = defineProps<{
summary: {
isTerminal: boolean;
jobCount: number;
errorCount: number;
errorCountText: string;
okCount: number;
okCountText: string;
runningCount: number;
runningCountText: string;
waitingCount: number;
waitingCountText: string;
};
}>();
</script>

<template>
<div class="collection-progress">
<b-progress v-if="!summary.isTerminal" :max="summary.jobCount">
<b-progress-bar
v-if="summary.errorCount"
v-b-tooltip.hover="summary.errorCountText"
:value="summary.errorCount"
<BProgress v-if="!props.summary.isTerminal" :max="props.summary.jobCount">
<BProgressBar
v-if="props.summary.errorCount"
v-b-tooltip.hover="props.summary.errorCountText"
:value="props.summary.errorCount"
variant="danger" />
<b-progress-bar
v-if="summary.okCount"
v-b-tooltip.hover="summary.okCountText"
:value="summary.okCount"
<BProgressBar
v-if="props.summary.okCount"
v-b-tooltip.hover="props.summary.okCountText"
:value="props.summary.okCount"
variant="success" />
<b-progress-bar
v-if="summary.runningCount"
v-b-tooltip.hover="summary.runningCountText"
:value="summary.runningCount"
<BProgressBar
v-if="props.summary.runningCount"
v-b-tooltip.hover="props.summary.runningCountText"
:value="props.summary.runningCount"
variant="warning" />
<b-progress-bar
v-if="summary.waitingCount"
v-b-tooltip.hover="summary.waitingCountText"
:value="summary.waitingCount"
<BProgressBar
v-if="props.summary.waitingCount"
v-b-tooltip.hover="props.summary.waitingCountText"
:value="props.summary.waitingCount"
variant="secondary" />
</b-progress>
</BProgress>
</div>
</template>
<script>
import { JobStateSummary } from "./JobStateSummary";

export default {
props: {
summary: { type: JobStateSummary, required: true },
},
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* Logic stolen from job-state-model.js and hdca-li.js
*/
import { STATES } from "../model/states";
import { states } from "../model/states";

const NON_TERMINAL_STATES = ["new", "waiting", "queued", "running", "resubmitted", "upload"];
const ERROR_STATES = ["error", "failed", "deleted"];
Expand All @@ -28,30 +28,30 @@ export class JobStateSummary extends Map {
get state() {
if (this.jobCount) {
if (this.isErrored) {
return STATES.error;
return states.error;
}
if (this.isRunning) {
return STATES.running;
return states.running;
}
if (this.isNew) {
return STATES.loading;
return states.loading;
}
if (this.isTerminal) {
return STATES.ok;
return states.ok;
}
return STATES.queued;
return states.queued;
}
return this.populated_state;
}

// Flags

get isNew() {
return !this.populated_state || this.populated_state == STATES.new;
return !this.populated_state || this.populated_state == states.new;
}

get isErrored() {
return this.populated_state == STATES.error || this.anyHasJobs(...ERROR_STATES);
return this.populated_state == states.error || this.anyHasJobs(...ERROR_STATES);
}

get isTerminal() {
Expand All @@ -62,7 +62,7 @@ export class JobStateSummary extends Map {
}

get isRunning() {
return this.hasJobs(STATES.running);
return this.hasJobs(states.running);
}

// Counts
Expand Down
48 changes: 36 additions & 12 deletions client/src/components/History/Content/ContentItem.test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
import { mount } from "@vue/test-utils";
import { getLocalVue } from "tests/jest/helpers";
import ContentItem from "./ContentItem";
import { updateContentFields } from "components/History/model/queries";

jest.mock("components/History/model/queries");
import ContentItem from "./ContentItem.vue";
import { updateContentFields } from "@/components/History/model/queries";
import { useRouter } from "vue-router/composables";
import { createTestingPinia } from "@pinia/testing";
import { setActivePinia } from "pinia";
import { useDataset } from "@/composables/dataset";

const localVue = getLocalVue();

// mock queries
jest.mock("@/components/History/model/queries");
updateContentFields.mockImplementation(async () => {});

jest.mock("vue-router/composables");
useRouter.mockReturnValue({
push: () => {},
});

jest.mock("@/composables/dataset");
useDataset.mockReturnValue({
isLoading: true,
dataset: null,
});

describe("ContentItem", () => {
let wrapper;
const testPinia = createTestingPinia();
setActivePinia(testPinia);

beforeEach(() => {
wrapper = mount(ContentItem, {
Expand All @@ -35,20 +51,25 @@ describe("ContentItem", () => {
localVue,
stubs: {
DatasetDetails: true,
vueTagsInput: false,
},
provide: {
store: {
dispatch: jest.fn,
getters: {},
},
},
pinia: testPinia,
});
});

it("check basics", async () => {
expect(wrapper.attributes("data-hid")).toBe("1");
it("applies id and name", () => {
const contentItem = wrapper.find(".content-item");

expect(contentItem.attributes("data-hid")).toBe("1");
expect(wrapper.find(".content-title").text()).toBe("name");
});

it("adds and removes tags", async () => {
const tags = wrapper.find(".stateless-tags").findAll(".tag");

// verify tags
Expand All @@ -61,7 +82,7 @@ describe("ContentItem", () => {
expect(wrapper.emitted()["tag-click"][i][0]).toBe(`tag${i + 1}`);
}

// close all tags
// remove all tags
for (let i = 0; i < 3; i++) {
const tagRemover = wrapper.find(`.tag[data-option=tag${i + 1}] button`);

Expand All @@ -71,18 +92,21 @@ describe("ContentItem", () => {

await wrapper.setProps({ isHistoryItem: false, item: { tags: [] } });
expect(wrapper.find(".stateless-tags").exists()).toBe(false);
});

// expansion button
it("expands", async () => {
const $el = wrapper.find(".cursor-pointer");
$el.trigger("click");
expect(wrapper.emitted()["update:expand-dataset"]).toBeDefined();
});

// select and unselect
it("is selectable", async () => {
const noSelector = wrapper.find(".selector > svg");
expect(noSelector.exists()).toBe(false);

const contentItem = wrapper.find(".content-item");
await wrapper.setProps({ selectable: true });
expect(wrapper.classes()).toEqual(expect.arrayContaining(["alert-success"]));
expect(contentItem.classes()).toEqual(expect.arrayContaining(["alert-success"]));

const selector = wrapper.find(".selector > svg");
expect(selector.attributes("data-icon")).toBe("square");
Expand All @@ -96,7 +120,7 @@ describe("ContentItem", () => {

await localVue.nextTick();
expect(wrapper.emitted()["update:selected"][1][0]).toBe(false);
expect(wrapper.classes()).toEqual(expect.arrayContaining(["alert-info"]));
expect(contentItem.classes()).toEqual(expect.arrayContaining(["alert-info"]));
expect(selector.attributes("data-icon")).toBe("check-square");
});
});
Loading