Skip to content

Commit 9033e73

Browse files
authored
Merge pull request #982 from traPtitech/fix/image-uploader-append
fix(client): append new images instead of replacing them in ImageUploader
2 parents 628b2c2 + fdebb22 commit 9033e73

File tree

2 files changed

+57
-18
lines changed

2 files changed

+57
-18
lines changed

client/src/views/shared/ImageUploader.vue

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div>
33
<v-file-input
4-
v-model="images"
4+
v-model="inputImages"
55
label="画像"
66
filled
77
multiple
@@ -18,25 +18,32 @@
1818
</template>
1919

2020
<script setup lang="ts">
21-
import { ref } from "vue";
21+
import { ref, nextTick } from "vue";
2222
2323
const images = defineModel<File[]>({ default: () => [] });
24+
const inputImages = ref<File[]>([]);
2425
const uploadImageUrl = ref<string[]>([]);
25-
const uploadImageBlob = ref<File[]>([]);
2626
2727
const imageChange = (files: File | File[]) => {
28-
uploadImageUrl.value = [];
29-
uploadImageBlob.value = [];
3028
if (!files) return;
3129
const fileList = Array.isArray(files) ? files : [files];
30+
if (fileList.length === 0) return;
31+
32+
const newImages = [...images.value];
33+
3234
fileList.forEach((file: File) => {
3335
const fr = new FileReader();
3436
fr.readAsDataURL(file);
35-
uploadImageBlob.value.push(file);
36-
images.value = uploadImageBlob.value;
37+
newImages.push(file);
3738
fr.addEventListener("load", () => {
3839
uploadImageUrl.value.push(fr.result as string);
3940
});
4041
});
42+
43+
images.value = newImages;
44+
45+
nextTick(() => {
46+
inputImages.value = [];
47+
});
4148
};
4249
</script>

client/tests/views/shared/ImageUploader.test.ts

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import ImageUploader from "@/views/shared/ImageUploader.vue";
22
import { mount } from "@vue/test-utils";
33
import { describe, expect, it, vi } from "vitest";
44
import * as components from "vuetify/components";
5+
56
describe("ImageUploader.vue", () => {
67
it("emits update:modelValue when images are selected", async () => {
78
const wrapper = mount(ImageUploader);
@@ -19,30 +20,61 @@ describe("ImageUploader.vue", () => {
1920
});
2021
}
2122

22-
// Stub global FileReader
2323
const originalFileReader = global.FileReader;
2424
vi.stubGlobal("FileReader", MockFileReader);
2525

26-
// Trigger image change
27-
// We can't easily trigger v-file-input change event directly in unit test without full mount
28-
// But we can call the handler if we could access it.
29-
// Since it's <script setup>, we can't access `imageChange` directly.
30-
// We will simulate the event on the component if possible, or verify structure.
31-
32-
// Ideally we should find the v-file-input and trigger update:modelValue
3326
const fileInput = wrapper.findComponent(components.VFileInput);
3427
expect(fileInput.exists()).toBe(true);
3528

36-
// Trigger the event that VFileInput would emit
3729
await fileInput.vm.$emit("update:modelValue", [file]);
3830

39-
// Wait for any async operations
4031
await new Promise(resolve => setTimeout(resolve, 0));
4132

4233
expect(wrapper.emitted("update:modelValue")).toBeTruthy();
4334
expect(wrapper.emitted("update:modelValue")![0][0]).toEqual([file]);
4435

45-
// Restore
36+
vi.stubGlobal("FileReader", originalFileReader);
37+
});
38+
39+
it("appends new images instead of replacing them", async () => {
40+
const wrapper = mount(ImageUploader);
41+
42+
const file1 = new File(["content1"], "test1.png", { type: "image/png" });
43+
const file2 = new File(["content2"], "test2.png", { type: "image/png" });
44+
45+
// Mock FileReader
46+
class MockFileReader {
47+
result = "data:image/png;base64,test";
48+
readAsDataURL = vi.fn();
49+
addEventListener = vi.fn((event, callback) => {
50+
if (event === "load") {
51+
callback();
52+
}
53+
});
54+
}
55+
56+
const originalFileReader = global.FileReader;
57+
vi.stubGlobal("FileReader", MockFileReader);
58+
59+
const fileInput = wrapper.findComponent(components.VFileInput);
60+
61+
// Simulate first upload
62+
await fileInput.vm.$emit("update:modelValue", [file1]);
63+
64+
let emitted = wrapper.emitted("update:modelValue");
65+
expect(emitted).toBeTruthy();
66+
expect(emitted!.slice(-1)[0][0]).toEqual([file1]);
67+
68+
// Simulate second upload
69+
await fileInput.vm.$emit("update:modelValue", [file2]);
70+
71+
emitted = wrapper.emitted("update:modelValue");
72+
const lastEmit = emitted!.slice(-1)[0][0] as File[];
73+
74+
expect(lastEmit).toHaveLength(2);
75+
expect(lastEmit).toContain(file1);
76+
expect(lastEmit).toContain(file2);
77+
4678
vi.stubGlobal("FileReader", originalFileReader);
4779
});
4880
});

0 commit comments

Comments
 (0)