Skip to content

Commit

Permalink
EW-694 Common Cartridge Course Import (#3010)
Browse files Browse the repository at this point in the history
* Adding new store module for the Common cartridge import dialog
* Adding new modal for the Common Cartridge import
---------

Co-authored-by: mkreuzkam-cap <[email protected]>
  • Loading branch information
psachmann and mkreuzkam-cap authored Mar 13, 2024
1 parent 55ad2bc commit b057bdf
Show file tree
Hide file tree
Showing 20 changed files with 2,874 additions and 2,209 deletions.
174 changes: 174 additions & 0 deletions src/components/molecules/CommonCartridgeImportModal.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import RoomsModule from "@/store/rooms";
import CommonCartridgeImportModal from "./CommonCartridgeImportModal.vue";
import { mount } from "@vue/test-utils";
import LoadingStateModule from "@/store/loading-state";
import NotifierModule from "@/store/notifier";
import {
COMMON_CARTRIDGE_IMPORT_MODULE_KEY,
LOADING_STATE_MODULE_KEY,
NOTIFIER_MODULE_KEY,
ROOMS_MODULE_KEY,
} from "@/utils/inject";
import { createModuleMocks } from "@/utils/mock-store-module";
import {
createTestingI18n,
createTestingVuetify,
} from "@@/tests/test-utils/setup";
import CommonCartridgeImportModule from "@/store/common-cartridge-import";

describe("@/components/molecules/CommonCartridgeImportModal", () => {
const setupWrapper = (getters: Partial<CommonCartridgeImportModule>) => {
document.body.setAttribute("data-app", "true");

const notifierModuleMock = createModuleMocks(NotifierModule);
const roomsModuleMock = createModuleMocks(RoomsModule, {
getAllElements: [],
});
const commonCartridgeImportModule = createModuleMocks(
CommonCartridgeImportModule,
getters
);

const wrapper = mount(CommonCartridgeImportModal, {
global: {
plugins: [createTestingVuetify(), createTestingI18n()],
provide: {
[LOADING_STATE_MODULE_KEY.valueOf()]:
createModuleMocks(LoadingStateModule),
[NOTIFIER_MODULE_KEY.valueOf()]: notifierModuleMock,
[ROOMS_MODULE_KEY.valueOf()]: roomsModuleMock,
[COMMON_CARTRIDGE_IMPORT_MODULE_KEY.valueOf()]:
commonCartridgeImportModule,
},
},
});

return {
wrapper,
roomsModuleMock,
notifierModuleMock,
commonCartridgeImportModule,
};
};

describe("when dialog is open", () => {
const setup = () => setupWrapper({ isOpen: true });

it("should contain disabled confirm button", async () => {
const { wrapper } = setup();

const confirmBtn = wrapper.findComponent(
"[data-testId='dialog-confirm-btn']"
) as any;

expect(confirmBtn.exists()).toBe(true);
expect(confirmBtn.isDisabled()).toBe(true);
});

it("should contain enabled cancel button", async () => {
const { wrapper } = setup();

const cancelBtn = wrapper.findComponent(
"[data-testid='dialog-cancel-btn']"
) as any;

expect(cancelBtn.exists()).toBe(true);
expect(cancelBtn.isDisabled()).toBe(false);
});

it("should contain file input", () => {
const { wrapper } = setup();

const fileInput = wrapper.findComponent(
"[data-testid='dialog-file-input']"
) as any;

expect(fileInput.exists()).toBe(true);
});
});

describe("when a file is selected", () => {
const setup = () =>
setupWrapper({
isOpen: true,
file: new File([], "file"),
});

it("should enable confirm button", () => {
const { wrapper } = setup();

const confirmBtn = wrapper.findComponent(
"[data-testId='dialog-confirm-btn']"
) as any;

expect(confirmBtn.isDisabled()).toBe(false);
});
});

describe("when file upload is successful", () => {
const setup = () =>
setupWrapper({
file: new File([], "file"),
isOpen: true,
isSuccess: true,
});

it("should show success message", async () => {
const { wrapper, roomsModuleMock, notifierModuleMock } = setup();
const confirmBtn = wrapper.findComponent(
"[data-testId='dialog-confirm-btn']"
);

await confirmBtn.trigger("click");

expect(roomsModuleMock.fetch).toHaveBeenCalledTimes(1);
expect(roomsModuleMock.fetchAllElements).toHaveBeenCalledTimes(1);
expect(notifierModuleMock.show).toHaveBeenCalledWith({
status: "success",
text: expect.any(String),
autoClose: true,
});
});
});

describe("when file upload is unsuccessful", () => {
const setup = () =>
setupWrapper({
file: new File([], "file"),
isOpen: true,
isSuccess: false,
});

it("should show error message", async () => {
const { wrapper, notifierModuleMock, roomsModuleMock } = setup();
const confirmBtn = wrapper.findComponent(
"[data-testId='dialog-confirm-btn']"
);

await confirmBtn.trigger("click");

expect(roomsModuleMock.fetch).not.toHaveBeenCalled();
expect(roomsModuleMock.fetchAllElements).not.toHaveBeenCalled();
expect(notifierModuleMock.show).toHaveBeenCalledWith({
status: "error",
text: expect.any(String),
autoClose: true,
});
});
});

describe("when dialog is closed", () => {
const setup = () => setupWrapper({ isOpen: true });

it("should reset the state", () => {
const { wrapper, commonCartridgeImportModule } = setup();
const cancelBtn = wrapper.findComponent(
"[data-testid='dialog-cancel-btn']"
);

cancelBtn.trigger("click");

expect(commonCartridgeImportModule.setIsOpen).toHaveBeenCalledWith(false);
});
});
});
137 changes: 137 additions & 0 deletions src/components/molecules/CommonCartridgeImportModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<template>
<v-dialog
ref="commonCartridgeImportModal"
v-model="isOpen"
:max-width="props.maxWidth"
@click:outside="onCancel()"
@keydown.esc="onCancel()"
data-testid="common-cartridge-import-modal"
>
<v-card :ripple="false">
<v-card-title>
<div ref="textTitle" class="text-h4 my-2 text-break">
{{ $t("pages.rooms.ccImportCourse.title") }}
</div>
</v-card-title>
<v-card-text class="text--primary">
<v-file-input
v-model="file"
:label="$t('pages.rooms.ccImportCourse.fileInputLabel')"
:prepend-icon="mdiTrayArrowUp"
accept=".imscc, .zip"
clearable
show-size
data-testid="dialog-file-input"
/>
</v-card-text>
<v-card-actions>
<v-spacer />
<div class="button-section">
<v-btn data-testid="dialog-cancel-btn" depressed @click="onCancel">
{{ $t("common.labels.close") }}
</v-btn>
</div>
<div class="button-section">
<v-btn
v-bind:disabled="importButtonDisabled"
v-on:click="onConfirm"
color="primary"
data-testid="dialog-confirm-btn"
>
{{ $t("pages.rooms.ccImportCourse.confirm") }}
</v-btn>
</div>
</v-card-actions>
</v-card>
</v-dialog>
</template>

<script setup lang="ts">
import { computed, defineProps, withDefaults } from "vue";
import { mdiTrayArrowUp } from "@mdi/js";
import { useI18n } from "vue-i18n";
import {
COMMON_CARTRIDGE_IMPORT_MODULE_KEY,
LOADING_STATE_MODULE_KEY,
NOTIFIER_MODULE_KEY,
ROOMS_MODULE_KEY,
injectStrict,
} from "@/utils/inject";
const i18n = useI18n();
const roomsModule = injectStrict(ROOMS_MODULE_KEY);
const loadingStateModule = injectStrict(LOADING_STATE_MODULE_KEY);
const notifierModule = injectStrict(NOTIFIER_MODULE_KEY);
const commonCartridgeImportModule = injectStrict(
COMMON_CARTRIDGE_IMPORT_MODULE_KEY
);
const props = withDefaults(
defineProps<{
maxWidth?: number;
}>(),
{
maxWidth: 480,
}
);
const file = computed<File[]>({
get: () =>
commonCartridgeImportModule.file ? [commonCartridgeImportModule.file] : [],
set: (value: File[]) => commonCartridgeImportModule.setFile(value[0]),
});
const isOpen = computed<boolean>({
get: () => commonCartridgeImportModule.isOpen,
set: (value: boolean) => commonCartridgeImportModule.setIsOpen(value),
});
const importButtonDisabled = computed(() => {
return file.value.length === 0;
});
function onCancel(): void {
file.value = [];
commonCartridgeImportModule.setIsOpen(false);
}
async function onConfirm(): Promise<void> {
const [selectedFile] = file.value;
commonCartridgeImportModule.setIsOpen(false);
loadingStateModule.open({
text: i18n.t("pages.rooms.ccImportCourse.loading"),
});
await commonCartridgeImportModule.importCommonCartridgeFile(selectedFile);
if (commonCartridgeImportModule.isSuccess) {
await Promise.allSettled([
roomsModule.fetch(),
roomsModule.fetchAllElements(),
]);
loadingStateModule.close();
const title = roomsModule.getAllElements[0]?.title;
notifierModule.show({
status: "success",
text: i18n.t("pages.rooms.ccImportCourse.success", { name: title }),
autoClose: true,
});
} else {
loadingStateModule.close();
notifierModule.show({
status: "error",
text: i18n.t("pages.rooms.ccImportCourse.error"),
autoClose: true,
});
}
file.value = [];
}
</script>

<style lang="scss" scoped>
.button-section {
margin-bottom: calc(var(--space-base-vuetify) * 2);
}
.button-section > button {
margin-left: calc(var(--space-base-vuetify) * 2);
}
</style>
5 changes: 4 additions & 1 deletion src/components/templates/DefaultWireframe.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
:icon="action.icon"
:href="action.href"
:to="action.to"
@click="action.customEvent"
@click="$emit('onFabItemClick', action.customEvent)"
>{{ action.label }}</speed-dial-menu-action
>
</template>
Expand Down Expand Up @@ -100,6 +100,9 @@ export default defineComponent({
default: null,
},
},
emits: {
onFabItemClick: (event: string) => (event ? true : false),
},
computed: {
showBorder(): boolean {
return !!(this.headline || this.$slots.header);
Expand Down
19 changes: 19 additions & 0 deletions src/components/templates/RoomWrapper.unit.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { authModule, roomsModule } from "@/store";
import { ComponentMountingOptions, mount } from "@vue/test-utils";
import RoomWrapper from "./RoomWrapper.vue";
import { createModuleMocks } from "@/utils/mock-store-module";
import setupStores from "@@/tests/test-utils/setupStores";
import RoomsModule from "@/store/rooms";
import AuthModule from "@/store/auth";
import EnvConfigModule from "@/store/env-config";
import {
COMMON_CARTRIDGE_IMPORT_MODULE_KEY,
LOADING_STATE_MODULE_KEY,
NOTIFIER_MODULE_KEY,
ROOMS_MODULE_KEY,
} from "@/utils/inject";
import LoadingStateModule from "@/store/loading-state";
import NotifierModule from "@/store/notifier";
import {
createTestingI18n,
createTestingVuetify,
} from "@@/tests/test-utils/setup";
import { SpeedDialMenu } from "@ui-speed-dial-menu";
import { meResponseFactory } from "@@/tests/test-utils";
import CommonCartridgeImportModule from "@/store/common-cartridge-import";

const getWrapper = (
options: ComponentMountingOptions<typeof RoomWrapper> = {
Expand All @@ -27,6 +37,15 @@ const getWrapper = (
},
},
...options,
provide: {
[LOADING_STATE_MODULE_KEY.valueOf()]:
createModuleMocks(LoadingStateModule),
[NOTIFIER_MODULE_KEY.valueOf()]: createModuleMocks(NotifierModule),
[ROOMS_MODULE_KEY.valueOf()]: createModuleMocks(RoomsModule),
[COMMON_CARTRIDGE_IMPORT_MODULE_KEY.valueOf()]: createModuleMocks(
CommonCartridgeImportModule
),
},
});
};

Expand Down
Loading

0 comments on commit b057bdf

Please sign in to comment.