diff --git a/App.Tests/AppTests.ts b/App.Tests/AppTests.ts index dd962930..0a541e11 100644 --- a/App.Tests/AppTests.ts +++ b/App.Tests/AppTests.ts @@ -1,6 +1,6 @@ -import { Test, TestSuite } from "xunit.ts"; +import {Test, TestSuite} from "xunit.ts"; import App from "../App/app.vue"; -import { shallowMount as mount } from "@vue/test-utils"; +import {shallowMount as mount} from "@vue/test-utils"; import Mockito from "ts-mockito"; import Renderer from "../App/Renderer"; import MockFactory from "./MockFactory"; @@ -14,16 +14,19 @@ export default class AppTests extends TestSuite { const canvas = MockFactory.canvas(); const renderer = new Renderer(Mockito.instance(canvas)); + const image_loader = MockFactory.imageLoader(); + //act const component = mount(App, { provide: { signal_service: () => Mockito.instance(signal_service), - renderer: () => renderer + renderer: () => renderer, + image_loader: () => Mockito.instance(image_loader) } }); //assert - this.assert.notEmpty(component.html()); + this.assert.notEmpty([...component.html()]); } @Test() @@ -35,11 +38,14 @@ export default class AppTests extends TestSuite { const canvas = MockFactory.canvas(); const renderer = new Renderer(Mockito.instance(canvas)); + const image_loader = MockFactory.imageLoader(); + //act const component = mount(App, { provide: { signal_service: () => Mockito.instance(signal_service), - renderer: () => renderer + renderer: () => renderer, + image_loader: () => Mockito.instance(image_loader) } }); await component.vm.$nextTick(); @@ -55,7 +61,13 @@ export default class AppTests extends TestSuite { const renderer = new Renderer(Mockito.instance(canvas)); //act - const component = mount(App, { provide: { signal_service: () => null, renderer: () => renderer } }); + const component = mount(App, { + provide: { + signal_service: () => null, + renderer: () => renderer, + image_loader: () => null + } + }); //assert this.assert.stringContains("loading", component.vm.connection_status); diff --git a/App.Tests/AppViewModelTests.ts b/App.Tests/AppViewModelTests.ts new file mode 100644 index 00000000..ed6bb591 --- /dev/null +++ b/App.Tests/AppViewModelTests.ts @@ -0,0 +1,28 @@ +import { Test, TestSuite } from "xunit.ts"; +import AppViewModel from "../App/AppViewModel"; +import Mockito from "ts-mockito"; +import ImageLoader from "../App/ImageLoader"; + +export default class AppViewModelTests extends TestSuite +{ + @Test() + async canSetBackgroundFromImageLoaderData() { + //arrange + const file = Mockito.mock(); + + const loader = Mockito.mock(); + Mockito.when(loader.loadImage(file)).thenResolve("data:image/png;base64,abc123"); + const vm = new AppViewModel(); + vm.image_loader = Mockito.instance(loader); + + const files = Mockito.mock(); + Mockito.when(files.length).thenReturn(1); + Mockito.when(files.item(0)).thenReturn(file); + + //act + await vm.setBackground(Mockito.instance(files)); + + //assert + this.assert.equal("data:image/png;base64,abc123", vm.background); + } +} \ No newline at end of file diff --git a/App.Tests/ImageLoaderTests.ts b/App.Tests/ImageLoaderTests.ts new file mode 100644 index 00000000..931786f9 --- /dev/null +++ b/App.Tests/ImageLoaderTests.ts @@ -0,0 +1,20 @@ +import {Test, TestSuite} from "xunit.ts"; +import ImageLoader from "../App/ImageLoader"; + +export default class ImageLoaderTests extends TestSuite { + @Test() + async canLoadImage() { + //arrange + const reader = new FileReader(); + const loader = new ImageLoader(reader); + + const blob = ["abc123"]; + const file = new File(blob, "test.txt", { type: "text/plain" }); + + //act + const base64 = await loader.loadImage(file); + + //assert + this.assert.equal("data:text/plain;base64,YWJjMTIz", base64); + } +} \ No newline at end of file diff --git a/App.Tests/MockFactory.ts b/App.Tests/MockFactory.ts index 6690c646..2c8073e8 100644 --- a/App.Tests/MockFactory.ts +++ b/App.Tests/MockFactory.ts @@ -1,5 +1,6 @@ import Mockito from "ts-mockito"; import SignalService from "../App/SignalService"; +import ImageLoader from "../App/ImageLoader"; export default class MockFactory { static signalService(): SignalService { @@ -28,4 +29,8 @@ export default class MockFactory { return canvas; } + + static imageLoader(): ImageLoader { + return Mockito.mock(); + } } \ No newline at end of file diff --git a/App/AppViewModel.ts b/App/AppViewModel.ts index 01da45f8..f2145d76 100644 --- a/App/AppViewModel.ts +++ b/App/AppViewModel.ts @@ -4,10 +4,9 @@ import Reading from "./Reading"; import AccessPoint from "./AccessPoint"; import Point from "./Point"; import AccessPointGrouping from "./AccessPointGrouping"; +import ImageLoader from "./ImageLoader"; export default class AppViewModel { - signal_service: SignalService | null = null; - renderer: Renderer | null = null; background: string = ""; pixelated: boolean = true; readings: Reading[] = []; @@ -16,6 +15,10 @@ export default class AppViewModel { group_by: AccessPointGrouping = new AccessPointGrouping(); debug: boolean = false; + signal_service: SignalService | null = null; + renderer: Renderer | null = null; + image_loader: ImageLoader | null = null; + async setBackground(files: FileList): Promise { if (files.length !== 1) { this.background = ""; @@ -23,14 +26,9 @@ export default class AppViewModel { } const file = files.item(0) as File; - const reader = new FileReader(); - const file_contents = new Promise((resolve, reject) => { - reader.onerror = () => reject(new DOMException("sad face")); - reader.onload = () => resolve(reader.result as string); - reader.readAsDataURL(file); - }); + const file_contents = this.image_loader?.loadImage(file); - this.background = await file_contents; + this.background = await file_contents ?? ""; } deleteDataPoint(index: number): void { diff --git a/App/Factory.ts b/App/Factory.ts index 740e2a0d..f880d2a8 100644 --- a/App/Factory.ts +++ b/App/Factory.ts @@ -1,13 +1,10 @@ import SignalService from "./SignalService"; -import { HubConnectionBuilder } from "@microsoft/signalr"; +import {HubConnectionBuilder} from "@microsoft/signalr"; import Renderer from "./Renderer"; import Signal from "./Signal"; +import ImageLoader from "./ImageLoader"; export default class Factory { - static renderer(canvas: HTMLCanvasElement) { - return new Renderer(canvas); - } - static signalService(signals: Signal[]): SignalService { const server = process.env.NODE_ENV === "production" ? "" : "http://localhost:5000"; @@ -18,4 +15,13 @@ export default class Factory { return new SignalService(connection, signals); } + + static renderer(canvas: HTMLCanvasElement): Renderer { + return new Renderer(canvas); + } + + static imageLoader(): ImageLoader { + const file_reader = new FileReader(); + return new ImageLoader(file_reader); + } } \ No newline at end of file diff --git a/App/ImageLoader.ts b/App/ImageLoader.ts new file mode 100644 index 00000000..a9744a37 --- /dev/null +++ b/App/ImageLoader.ts @@ -0,0 +1,16 @@ +export default class ImageLoader { + + private readonly file_reader: FileReader; + + constructor(file_reader: FileReader) { + this.file_reader = file_reader; + } + + async loadImage(file: Blob) { + return new Promise((resolve, reject) => { + this.file_reader.onerror = () => reject(new DOMException("Could not read file")); + this.file_reader.onload = () => resolve(this.file_reader.result as string); + this.file_reader.readAsDataURL(file); + }); + } +} \ No newline at end of file diff --git a/App/app.ts b/App/app.ts index 45a2c1a0..8854d89d 100644 --- a/App/app.ts +++ b/App/app.ts @@ -1,7 +1,6 @@ import Vue from "vue"; import app from "./app.vue"; import Factory from "./Factory"; -import Signal from "./Signal"; Vue.config.devtools = true; @@ -9,7 +8,8 @@ export default new Vue({ el: "app", render: (r: Vue.CreateElement) => r(app), provide: { - signal_service: (signals: Signal[]) => Factory.signalService(signals), - renderer: (canvas: HTMLCanvasElement) => Factory.renderer(canvas) + signal_service: Factory.signalService, + renderer: Factory.renderer, + image_loader: Factory.imageLoader } }); \ No newline at end of file diff --git a/App/app.vue b/App/app.vue index 713188a7..485c988d 100644 --- a/App/app.vue +++ b/App/app.vue @@ -9,40 +9,42 @@