Skip to content

Commit

Permalink
Split image loading into its own class
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveDesmond-ca committed Dec 16, 2021
1 parent 896d663 commit 7f406ad
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 34 deletions.
24 changes: 18 additions & 6 deletions App.Tests/AppTests.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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()
Expand All @@ -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();
Expand All @@ -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);
Expand Down
28 changes: 28 additions & 0 deletions App.Tests/AppViewModelTests.ts
Original file line number Diff line number Diff line change
@@ -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<File>();

const loader = Mockito.mock<ImageLoader>();
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<FileList>();
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);
}
}
20 changes: 20 additions & 0 deletions App.Tests/ImageLoaderTests.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
5 changes: 5 additions & 0 deletions App.Tests/MockFactory.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -28,4 +29,8 @@ export default class MockFactory {

return canvas;
}

static imageLoader(): ImageLoader {
return Mockito.mock<ImageLoader>();
}
}
16 changes: 7 additions & 9 deletions App/AppViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = [];
Expand All @@ -16,21 +15,20 @@ 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<void> {
if (files.length !== 1) {
this.background = "";
return;
}

const file = files.item(0) as File;
const reader = new FileReader();
const file_contents = new Promise<string>((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 {
Expand Down
16 changes: 11 additions & 5 deletions App/Factory.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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);
}
}
16 changes: 16 additions & 0 deletions App/ImageLoader.ts
Original file line number Diff line number Diff line change
@@ -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<string>((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);
});
}
}
6 changes: 3 additions & 3 deletions App/app.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Vue from "vue";
import app from "./app.vue";
import Factory from "./Factory";
import Signal from "./Signal";

Vue.config.devtools = true;

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
}
});
24 changes: 13 additions & 11 deletions App/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,42 @@

<script lang="ts">
import Vue from "vue";
import Status from './status.vue';
import HeaderMenu from './header-menu.vue';
import MainArea from './main-area.vue';
import DebugPanel from './debug-panel.vue';
import Status from "./status.vue";
import HeaderMenu from "./header-menu.vue";
import MainArea from "./main-area.vue";
import DebugPanel from "./debug-panel.vue";
import SharedState from "./SharedState";
export default Vue.extend({
components: {
'status': Status,
'header-menu': HeaderMenu,
'main-area': MainArea,
'debug-panel': DebugPanel
"status": Status,
"header-menu": HeaderMenu,
"main-area": MainArea,
"debug-panel": DebugPanel
},
inject: {
signal_service_factory: "signal_service",
renderer_factory: "renderer",
renderer_factory: "renderer",
image_loader_factory: "image_loader",
},
data: () => SharedState,
computed: {
connection_status(): string {
return this.signal_service != null
? this.signal_service.status
: '(loading...)'
: "(loading...)"
},
last_updated(): string {
return this.signal_service != null
? this.signal_service.last_updated
: '';
: "";
}
},
mounted(): void {
const canvas = document.getElementById("webglcanvas") as HTMLCanvasElement;
this.signal_service = this.signal_service_factory(this.current.signals);
this.renderer = this.renderer_factory(canvas);
this.image_loader = this.image_loader_factory();
},
watch: {
selected(): void {
Expand Down

0 comments on commit 7f406ad

Please sign in to comment.