fix: Catch error while tfrecord image not loaded (#701)

Fixes bug where tfrecords show error loading image

Resolves AB#17680
Esse commit está contido em:
Phil
2019-03-27 18:20:05 -07:00
commit de Wallace Breza
commit d151292328
7 arquivos alterados com 103 adições e 22 exclusões
+4
Ver Arquivo
@@ -133,3 +133,7 @@
cursor: pointer;
}
}
.Toastify .vott-toast-container {
top: 3em;
}
+1 -1
Ver Arquivo
@@ -86,7 +86,7 @@ export default class App extends React.Component<IAppProps> {
<StatusBar>
<StatusBarMetrics project={this.props.currentProject} />
</StatusBar>
<ToastContainer />
<ToastContainer className="vott-toast-container" />
</div>
</Router >
</KeyboardManager>
+14
Ver Arquivo
@@ -123,6 +123,20 @@ export default class MockFactory {
},
};
break;
case AssetType.TFRecord:
testAsset = {
id: `tfrecordasset-${name}`,
format: "tfrecord",
name: `tfrecordasset-${name}.tfrecord`,
path: `${path}.tfrecord`,
state: assetState,
type: assetType,
size: {
width: 800,
height: 600,
},
};
break;
default:
testAsset = {
id: `asset-${name}`,
@@ -4,7 +4,8 @@ import { AssetPreview, IAssetPreviewProps, IAssetPreviewState } from "./assetPre
import MockFactory from "../../../../common/mockFactory";
import { ImageAsset } from "./imageAsset";
import { VideoAsset } from "./videoAsset";
import { AssetType } from "../../../../models/applicationState";
import { AssetType, AssetState } from "../../../../models/applicationState";
import { TFRecordAsset } from "./tfrecordAsset";
describe("Asset Preview Component", () => {
let wrapper: ReactWrapper<IAssetPreviewProps, IAssetPreviewState> = null;
@@ -48,6 +49,19 @@ describe("Asset Preview Component", () => {
expect(wrapper.find(VideoAsset).exists()).toBe(true);
});
it("renders a tfrecord asset when asset type is tfrecord", () => {
const props: IAssetPreviewProps = {
...defaultProps,
asset: MockFactory.createTestAsset("test-record-asset",
AssetState.Visited,
dataUri,
AssetType.TFRecord),
};
wrapper = createComponent(props);
expect(wrapper.find(TFRecordAsset).exists()).toBe(true);
expect(wrapper.instance().state).toMatchObject({hasError: false});
});
it("renders loading indicator if asset isn't fully loaded", () => {
wrapper = createComponent();
expect(wrapper.find(".asset-loading").exists()).toBe(true);
@@ -1,7 +1,7 @@
import React from "react";
import { IAssetProps } from "./assetPreview";
import { ReactWrapper, mount } from "enzyme";
import { TFRecordAsset } from "./tfrecordAsset";
import { TFRecordAsset, ITFRecordState } from "./tfrecordAsset";
import MockFactory from "../../../../common/mockFactory";
import { TFRecordsBuilder, FeatureType } from "../../../../providers/export/tensorFlowRecords/tensorFlowBuilder";
import HtmlFileReader from "../../../../common/htmlFileReader";
@@ -10,9 +10,13 @@ describe("TFRecord Asset Component", () => {
// tslint:disable-next-line:max-line-length
const dataImage64 = "R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7";
const dataImage = new Uint8Array(Buffer.from(dataImage64, "base64"));
const dataUri = "data:image;base64," + dataImage64;
let wrapper: ReactWrapper<IAssetProps> = null;
const onLoadHandler = jest.fn();
const onErrorHandler = jest.fn();
let tfrecords: Buffer;
beforeEach(() => {
let builder: TFRecordsBuilder;
@@ -21,37 +25,34 @@ describe("TFRecord Asset Component", () => {
const buffer = builder.build();
tfrecords = TFRecordsBuilder.buildTFRecords([buffer]);
onLoadHandler.mockClear();
onErrorHandler.mockClear();
});
HtmlFileReader.getAssetArray = jest.fn((asset) => {
return Promise.resolve<Uint8Array>(new Uint8Array(tfrecords));
return Promise.resolve<ArrayBuffer>(new Uint8Array(tfrecords));
});
let wrapper: ReactWrapper<IAssetProps> = null;
const onLoadHandler = jest.fn();
MockFactory.createTestAsset("test");
const defaultProps: IAssetProps = {
asset: {
...MockFactory.createTestAsset("test"),
path: "abc",
},
onLoaded: onLoadHandler,
onError: onErrorHandler,
};
function createComponent(props?: IAssetProps): ReactWrapper<IAssetProps> {
function createComponent(props?: IAssetProps): ReactWrapper<IAssetProps, ITFRecordState> {
props = props || defaultProps;
return mount(<TFRecordAsset {...props} />);
}
function wait() {
return new Promise((resolve) => setImmediate(resolve));
}
it("load image correctly", async () => {
const props = { ...defaultProps };
wrapper = createComponent(props);
await wait();
await MockFactory.flushUi();
const img = wrapper.find("img");
expect(img.exists()).toBe(true);
@@ -63,11 +64,39 @@ describe("TFRecord Asset Component", () => {
it("raises onLoad handler when image has completed loading", async () => {
wrapper = createComponent();
await wait();
await MockFactory.flushUi();
const img = wrapper.find("img").getDOMNode() as HTMLImageElement;
img.dispatchEvent(new Event("load"));
expect(onLoadHandler).toBeCalledWith(expect.any(HTMLImageElement));
});
it("raises onError handler when the image has an error", async () => {
wrapper = createComponent();
await MockFactory.flushUi();
const img = wrapper.find("img").getDOMNode() as HTMLImageElement;
img.dispatchEvent(new Event("error"));
expect(onErrorHandler).toBeCalled();
});
it("raises onError handler when there is an error reading image data from tf record", async () => {
HtmlFileReader.getAssetArray = jest.fn(() => Promise.resolve());
wrapper = createComponent();
await MockFactory.flushUi();
expect(onErrorHandler).toBeCalled();
});
it("does not raise onError handler when there the tf record has not yet been read", async () => {
wrapper = createComponent();
const img = wrapper.find("img").getDOMNode() as HTMLImageElement;
img.dispatchEvent(new Event("error"));
expect(onErrorHandler).not.toBeCalled();
});
});
@@ -1,4 +1,4 @@
import React, { SyntheticEvent } from "react";
import React from "react";
import { IAssetProps } from "./assetPreview";
import { IAsset } from "../../../../models/applicationState";
import HtmlFileReader from "../../../../common/htmlFileReader";
@@ -8,9 +8,11 @@ import { FeatureType } from "../../../../providers/export/tensorFlowRecords/tens
/**
* State for TFRecord Asset Image component
* @member tfRecordImage64 - base64 representation of the image data
* @member hasError - Whether or not there was an error loading the image data from the tf record
*/
export interface ITFRecordState {
tfRecordImage64: string;
hasError: boolean;
}
/**
@@ -19,6 +21,7 @@ export interface ITFRecordState {
export class TFRecordAsset extends React.Component<IAssetProps, ITFRecordState> {
public state: ITFRecordState = {
tfRecordImage64: "",
hasError: false,
};
private image: React.RefObject<HTMLImageElement> = React.createRef();
@@ -28,7 +31,7 @@ export class TFRecordAsset extends React.Component<IAssetProps, ITFRecordState>
<img ref={this.image}
src={this.state.tfRecordImage64}
onLoad={this.onLoad}
onError={this.props.onError} />
onError={this.onError} />
);
}
@@ -42,10 +45,20 @@ export class TFRecordAsset extends React.Component<IAssetProps, ITFRecordState>
}
}
private async updateImage() {
this.setState({
tfRecordImage64: await this.getTFRecordBase64Image(this.props.asset),
});
private updateImage = async (): Promise<void> => {
try {
const base64ImageData = await this.getTFRecordBase64Image(this.props.asset);
this.setState({
tfRecordImage64: base64ImageData,
hasError: !(!!base64ImageData),
});
} catch (e) {
this.setState({
hasError: true,
});
this.onError(e);
}
}
private onLoad = () => {
@@ -54,6 +67,12 @@ export class TFRecordAsset extends React.Component<IAssetProps, ITFRecordState>
}
}
private onError = (e: React.SyntheticEvent<Element>) => {
if (this.props.onError && (this.state.tfRecordImage64 || this.state.hasError)) {
this.props.onError(e);
}
}
private async getTFRecordBase64Image(asset: IAsset): Promise<string> {
const tfrecords = new Buffer(await HtmlFileReader.getAssetArray(asset));
const reader = new TFRecordsReader(tfrecords);
@@ -8,7 +8,7 @@ import EditorPage, { IEditorPageProps, IEditorPageState } from "./editorPage";
import MockFactory from "../../../../common/mockFactory";
import {
IApplicationState, IAssetMetadata, IProject,
EditorMode, IAsset, AssetState, ISize,
EditorMode, IAsset, AssetState, AssetType, ISize,
} from "../../../../models/applicationState";
import { AssetProviderFactory } from "../../../../providers/storage/assetProviderFactory";
import createReduxStore from "../../../../redux/store/store";
@@ -385,6 +385,7 @@ describe("Editor Page Component", () => {
expect(matchingRootAsset.state).toEqual(AssetState.Tagged);
});
});
describe("Basic toolbar test and hotkey tests", () => {
let wrapper: ReactWrapper = null;
let editorPage: ReactWrapper<IEditorPageProps, IEditorPageState> = null;