Skip to content

Commit

Permalink
Fix/2294/extract initial file upload into migrated service and fix lo…
Browse files Browse the repository at this point in the history
…ose promise in test (#3110)

extract / refactor out LoadInitialFileService
fix Unhandled Promise in tests #2294
ref Wrong calculation for initial file size #3111
ref  Migrate all Angular Components and Services #2318
  • Loading branch information
shaman-apprentice committed Oct 31, 2022
1 parent e220c8b commit 139cb2e
Show file tree
Hide file tree
Showing 25 changed files with 477 additions and 611 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/)
- Migrate codeMap.render.service, codeMap.label.service, codeMap.mouseEvent.service, codeMap.arrow.service, codeCharta.service, sharpnessMode.service, isLoadingFile.service, scaling.service and experimentalFeaturesEnabled.service to Angular [#3094](https://github.com/MaibornWolff/codecharta/pull/3094)
- Extract tree map size into a const as this is never changed [3098](https://github.com/MaibornWolff/codecharta/pull/3098)
- Migrate markedPackages.service, edges.service, blacklist.service, files.service and injector.service [3106](https://github.com/MaibornWolff/codecharta/pull/3106)
- Refactor out `LoadInitialFileService` and fix with it loose Promise in tests [#3110](https://github.com/MaibornWolff/codecharta/pull/3110)

## [1.109.1] - 2022-10-12

Expand Down
2 changes: 2 additions & 0 deletions visualization/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { APP_INITIALIZER, Inject, NgModule } from "@angular/core"
import { BrowserModule } from "@angular/platform-browser"
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"
import { UpgradeModule } from "@angular/upgrade/static"
import { HttpClientModule } from "@angular/common/http"
import { FormsModule, ReactiveFormsModule } from "@angular/forms"
import { MaterialModule } from "./material/material.module"
import { AttributeSideBarModule } from "./codeCharta/ui/attributeSideBar/attributeSideBar.module"
Expand Down Expand Up @@ -45,6 +46,7 @@ import { UpdateFileSettingsEffect } from "./codeCharta/state/effects/updateFileS
imports: [
BrowserModule,
UpgradeModule,
HttpClientModule,
EffectsModule.forRoot([
UnfocusNodesEffect,
AddBlacklistItemsIfNotResultsInEmptyMapEffect,
Expand Down
235 changes: 7 additions & 228 deletions visualization/app/codeCharta/codeCharta.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,27 @@
import "./codeCharta.module"
import { TestBed } from "@angular/core/testing"
import { IHttpService, ILocationService } from "angular"
import { CodeChartaService } from "./codeCharta.service"
import { CodeChartaController } from "./codeCharta.component"
import { getService, instantiateModule } from "../../mocks/ng.mockhelper"
import { StoreService } from "./state/store.service"
import { setAppSettings } from "./state/store/appSettings/appSettings.actions"
import { ThreeCameraService } from "./ui/codeMap/threeViewer/threeCamera.service"
import sample1 from "./assets/sample1.cc.json"
import sample2 from "./assets/sample2.cc.json"
import { CCFile, LayoutAlgorithm } from "./codeCharta.model"
import { GlobalSettingsHelper } from "./util/globalSettingsHelper"
import { GLOBAL_SETTINGS } from "./util/dataMocks"
import { setIsWhiteBackground } from "./state/store/appSettings/isWhiteBackground/isWhiteBackground.actions"
import { setResetCameraIfNewFileIsLoaded } from "./state/store/appSettings/resetCameraIfNewFileIsLoaded/resetCameraIfNewFileIsLoaded.actions"
import { setHideFlatBuildings } from "./state/store/appSettings/hideFlatBuildings/hideFlatBuildings.actions"
import { setExperimentalFeaturesEnabled } from "./state/store/appSettings/enableExperimentalFeatures/experimentalFeaturesEnabled.actions"
import { setLayoutAlgorithm } from "./state/store/appSettings/layoutAlgorithm/layoutAlgorithm.actions"
import { setMaxTreeMapFiles } from "./state/store/appSettings/maxTreeMapFiles/maxTreeMapFiles.actions"
import { FileSelectionState, FileState } from "./model/files/files"
import { setFiles } from "./state/store/files/files.actions"
import { MatDialog } from "@angular/material/dialog"
import { ErrorDialogComponent } from "./ui/dialogs/errorDialog/errorDialog.component"
import { LoadInitialFileService } from "./services/loadInitialFile/loadInitialFile.service"

describe("codeChartaController", () => {
let codeChartaController: CodeChartaController
let threeCameraService: ThreeCameraService
let $location: ILocationService
let $http: IHttpService

let storeService: StoreService
let dialog: MatDialog
let codeChartaService: CodeChartaService
let loadInitialFileService: LoadInitialFileService

function restartSystem() {
instantiateModule("app.codeCharta")

$location = getService<ILocationService>("$location")
$http = getService<IHttpService>("$http")
storeService = getService<StoreService>("storeService")
threeCameraService = TestBed.inject(ThreeCameraService)
dialog = { open: jest.fn() } as unknown as MatDialog
codeChartaService = { loadFiles: jest.fn() } as unknown as CodeChartaService
loadInitialFileService = { loadFileOrSample: jest.fn() } as unknown as LoadInitialFileService
}

function rebuildController() {
codeChartaController = new CodeChartaController($location, $http, storeService, dialog, codeChartaService)
}

function withMockedUrlUtils() {
codeChartaController["urlUtils"] = jest.fn().mockReturnValue({
getFileDataFromQueryParam: jest.fn().mockReturnValue(Promise.resolve([])),
getParameterByName: jest.fn().mockReturnValue(true)
})()
new CodeChartaController(storeService, loadInitialFileService)
}

function initThreeCameraService() {
Expand All @@ -61,214 +31,23 @@ describe("codeChartaController", () => {

function initialize() {
restartSystem()
rebuildController()
initThreeCameraService()
withMockedUrlUtils()
localStorage.clear()
}

describe("constructor", () => {
beforeEach(() => {
initialize()
})
it("should set urlUtils", () => {
rebuildController()

expect(codeChartaController["urlUtils"]).toBeDefined()
})

it("should show loading file gif", () => {
rebuildController()

expect(storeService.getState().appSettings.isLoadingFile).toBeTruthy()
})
})

describe("loadFileOrSample", () => {
beforeEach(() => {
initialize()
codeChartaController.tryLoadingSampleFiles = jest.fn()
})

it("should call tryLoadingSampleFiles when data is an empty array", async () => {
// hot fix for issue #2294
it("should load file or sample", () => {
rebuildController()
codeChartaController.tryLoadingSampleFiles = jest.fn()

await codeChartaController.loadFileOrSample()

expect(codeChartaController.tryLoadingSampleFiles).toHaveBeenCalledWith(new Error("Filename is missing"))
})

it("should call loadFiles when data is not an empty array", async () => {
codeChartaController["urlUtils"].getFileDataFromQueryParam = jest.fn().mockReturnValue(Promise.resolve([{}]))

await codeChartaController.loadFileOrSample()

expect(codeChartaService.loadFiles).toHaveBeenCalledWith([{}])
})

it("should call storeService.dispatch if loadFiles-Promise resolves", async () => {
codeChartaController["urlUtils"].getFileDataFromQueryParam = jest.fn().mockReturnValue(Promise.resolve([{}]))
storeService.dispatch = jest.fn()

await codeChartaController.loadFileOrSample()

expect(storeService.dispatch).toHaveBeenCalledWith(setAppSettings())
})

it("should set the default global settings if localStorage does not exist", async () => {
codeChartaController["urlUtils"].getFileDataFromQueryParam = jest.fn().mockReturnValue(Promise.resolve([{}]))

await codeChartaController.loadFileOrSample()

expect(storeService.getState().appSettings.hideFlatBuildings).toBeFalsy()
expect(storeService.getState().appSettings.isWhiteBackground).toBeFalsy()
expect(storeService.getState().appSettings.resetCameraIfNewFileIsLoaded).toBeTruthy()
expect(storeService.getState().appSettings.experimentalFeaturesEnabled).toBeFalsy()
expect(storeService.getState().appSettings.layoutAlgorithm).toEqual(LayoutAlgorithm.SquarifiedTreeMap)
expect(storeService.getState().appSettings.maxTreeMapFiles).toEqual(100)
})

it("should set the global settings from localStorage", async () => {
GlobalSettingsHelper.setGlobalSettingsInLocalStorage(GLOBAL_SETTINGS)
codeChartaController["urlUtils"].getFileDataFromQueryParam = jest.fn().mockReturnValue(Promise.resolve([{}]))

await codeChartaController.loadFileOrSample()

expect(storeService.getState().appSettings.hideFlatBuildings).toBeTruthy()
expect(storeService.getState().appSettings.isWhiteBackground).toBeTruthy()
expect(storeService.getState().appSettings.resetCameraIfNewFileIsLoaded).toBeTruthy()
expect(storeService.getState().appSettings.experimentalFeaturesEnabled).toBeTruthy()
expect(storeService.getState().appSettings.layoutAlgorithm).toEqual(LayoutAlgorithm.SquarifiedTreeMap)
expect(storeService.getState().appSettings.maxTreeMapFiles).toEqual(50)
})
})

describe("tryLoadingSampleFiles", () => {
beforeEach(() => {
initialize()
localStorage.clear()
})
it("should call getParameterByName with 'file'", () => {
codeChartaController.tryLoadingSampleFiles(new Error("Ignored"))

expect(codeChartaController["urlUtils"].getParameterByName).toHaveBeenCalledWith("file")
})

it("should call showErrorDialog when no file is found", () => {
codeChartaController.tryLoadingSampleFiles(new Error("Actual error message"))
expect(dialog.open).toHaveBeenCalledWith(ErrorDialogComponent, {
data: {
title: "Error (Actual error message)",
message: "One or more files from the given file URL parameter could not be loaded. Loading sample files instead."
}
})
})

it("should call loadFiles with sample files", () => {
const expected = [
{ fileName: "sample1.cc.json", content: sample1, fileSize: 3072 },
{ fileName: "sample2.cc.json", content: sample2, fileSize: 2048 }
]

codeChartaController.tryLoadingSampleFiles(new Error("Ignored"))

expect(codeChartaService.loadFiles).toHaveBeenCalledWith(expected)
})
it("should set the default global settings for the sample files if localStorage does not exist", () => {
codeChartaController.tryLoadingSampleFiles(new Error("Ignored"))

expect(storeService.getState().appSettings.hideFlatBuildings).toBeFalsy()
expect(storeService.getState().appSettings.isWhiteBackground).toBeFalsy()
expect(storeService.getState().appSettings.resetCameraIfNewFileIsLoaded).toBeTruthy()
expect(storeService.getState().appSettings.experimentalFeaturesEnabled).toBeFalsy()
expect(storeService.getState().appSettings.layoutAlgorithm).toEqual(LayoutAlgorithm.SquarifiedTreeMap)
expect(storeService.getState().appSettings.maxTreeMapFiles).toEqual(100)
})

it("should set the global settings from localStorage for sample files", () => {
GlobalSettingsHelper.setGlobalSettingsInLocalStorage(GLOBAL_SETTINGS)

codeChartaController.tryLoadingSampleFiles(new Error("Ignored"))

expect(storeService.getState().appSettings.hideFlatBuildings).toBeTruthy()
expect(storeService.getState().appSettings.isWhiteBackground).toBeTruthy()
expect(storeService.getState().appSettings.resetCameraIfNewFileIsLoaded).toBeTruthy()
expect(storeService.getState().appSettings.experimentalFeaturesEnabled).toBeTruthy()
expect(storeService.getState().appSettings.layoutAlgorithm).toEqual(LayoutAlgorithm.SquarifiedTreeMap)
expect(storeService.getState().appSettings.maxTreeMapFiles).toEqual(50)
})

it("should not dispatch a global setting from localStorage if the setting is currently the same", () => {
storeService.dispatch(setHideFlatBuildings(true))
storeService.dispatch(setIsWhiteBackground(true))
storeService.dispatch(setResetCameraIfNewFileIsLoaded(true))
storeService.dispatch(setExperimentalFeaturesEnabled(true))
storeService.dispatch(setLayoutAlgorithm(LayoutAlgorithm.SquarifiedTreeMap))
storeService.dispatch(setMaxTreeMapFiles(50))
storeService.dispatch = jest.fn()
GlobalSettingsHelper.setGlobalSettingsInLocalStorage(GLOBAL_SETTINGS)

GlobalSettingsHelper.setGlobalSettingsOfLocalStorageIfExists(storeService)

expect(storeService.dispatch).not.toHaveBeenCalled()
})

it("should set files to multiple mode when no 'mode' parameter is given", () => {
const fileState: FileState[] = [{ file: {} as CCFile, selectedAs: FileSelectionState.None }]
storeService.dispatch(setFiles(fileState))
storeService.dispatch = jest.fn()
codeChartaController["urlUtils"].getParameterByName = jest.fn().mockReturnValue(null)

codeChartaController["setRenderStateFromUrl"]()

expect(storeService.dispatch).toHaveBeenCalledWith({ payload: [{}], type: "SET_STANDARD" })
})

it("should set files to multiple mode when empty string is given", () => {
const fileState: FileState[] = [{ file: {} as CCFile, selectedAs: FileSelectionState.None }]
storeService.dispatch(setFiles(fileState))
storeService.dispatch = jest.fn()
codeChartaController["urlUtils"].getParameterByName = jest.fn().mockReturnValue("")

codeChartaController["setRenderStateFromUrl"]()

expect(storeService.dispatch).toHaveBeenCalledWith({
payload: [{}],
type: "SET_STANDARD"
})
})

it("should set files to multiple mode when any string (except 'Delta') is given", () => {
const fileState: FileState[] = [{ file: {} as CCFile, selectedAs: FileSelectionState.None }]
storeService.dispatch(setFiles(fileState))
storeService.dispatch = jest.fn()
codeChartaController["urlUtils"].getParameterByName = jest.fn().mockReturnValue("invalidMode")

codeChartaController["setRenderStateFromUrl"]()

expect(storeService.dispatch).toHaveBeenCalledWith({
payload: [{}],
type: "SET_STANDARD"
})
})

it("should set files to delta mode when 'mode=delta' parameter is given", () => {
const fileState: FileState[] = [
{ file: {} as CCFile, selectedAs: FileSelectionState.None },
{ file: {} as CCFile, selectedAs: FileSelectionState.None }
]
storeService.dispatch(setFiles(fileState))
storeService.dispatch = jest.fn()
codeChartaController["urlUtils"].getParameterByName = jest.fn().mockReturnValue("Delta")

codeChartaController["setRenderStateFromUrl"]()

expect(storeService.dispatch).toHaveBeenCalledWith({
payload: { comparisonFile: {}, referenceFile: {} },
type: "SET_DELTA"
})
expect(loadInitialFileService.loadFileOrSample).toHaveBeenCalled()
})
})
})
75 changes: 3 additions & 72 deletions visualization/app/codeCharta/codeCharta.component.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import { UrlExtractor } from "./util/urlExtractor"
import { IHttpService, ILocationService } from "angular"
import "./codeCharta.component.scss"
import { CodeChartaService } from "./codeCharta.service"
import { NameDataPair } from "./codeCharta.model"
import { StoreService } from "./state/store.service"
import { setAppSettings } from "./state/store/appSettings/appSettings.actions"
import { setIsLoadingFile } from "./state/store/appSettings/isLoadingFile/isLoadingFile.actions"
import packageJson from "../../package.json"
import { setDelta, setStandard } from "./state/store/files/files.actions"
import { getCCFiles } from "./model/files/files.helper"
import sample1 from "./assets/sample1.cc.json"
import sample2 from "./assets/sample2.cc.json"
import { ExportCCFile } from "./codeCharta.api.model"
import { GlobalSettingsHelper } from "./util/globalSettingsHelper"
import { MatDialog } from "@angular/material/dialog"
import { ErrorDialogComponent } from "./ui/dialogs/errorDialog/errorDialog.component"
import { LoadInitialFileService } from "./services/loadInitialFile/loadInitialFile.service"

export class CodeChartaController {
private _viewModel: {
Expand All @@ -23,68 +11,11 @@ export class CodeChartaController {
version: "version unavailable"
}

private urlUtils: UrlExtractor

constructor(
private $location: ILocationService,
private $http: IHttpService,
private storeService: StoreService,
private dialog: MatDialog,
private codeChartaService: CodeChartaService
) {
constructor(private storeService: StoreService, private loadInitialFileService: LoadInitialFileService) {
"ngInject"
this._viewModel.version = packageJson.version
this.urlUtils = new UrlExtractor(this.$location, this.$http)
this.storeService.dispatch(setIsLoadingFile(true))
this.loadFileOrSample()
}

async loadFileOrSample() {
try {
const data = await this.urlUtils.getFileDataFromQueryParam()
this.tryLoadingFiles(data)
this.setRenderStateFromUrl()
} catch (error) {
this.tryLoadingSampleFiles(error)
}
}

tryLoadingSampleFiles(error: Error & { statusText?: string; status?: number }) {
if (this.urlUtils.getParameterByName("file")) {
const message = "One or more files from the given file URL parameter could not be loaded. Loading sample files instead."
let title = "Error"
if (error.message) {
title += ` (${error.message})`
} else if (error.statusText && error.status) {
title += ` (${error.status}: ${error.statusText})`
}
this.dialog.open(ErrorDialogComponent, {
data: { title, message }
})
}
this.tryLoadingFiles([
{ fileName: "sample1.cc.json", fileSize: 3 * 1024, content: sample1 as ExportCCFile },
{ fileName: "sample2.cc.json", fileSize: 2 * 1024, content: sample2 as ExportCCFile }
])
}

private tryLoadingFiles(values: NameDataPair[]) {
this.storeService.dispatch(setAppSettings())
GlobalSettingsHelper.setGlobalSettingsOfLocalStorageIfExists(this.storeService)
this.codeChartaService.loadFiles(values)
}

// TODO: Please make sure that this function works fine on Github pages with
// the updated file selection (no more single mode!)
private setRenderStateFromUrl() {
const renderState = this.urlUtils.getParameterByName("mode")
const files = getCCFiles(this.storeService.getState().files)

if (renderState === "Delta" && files.length >= 2) {
this.storeService.dispatch(setDelta(files[0], files[1]))
} else {
this.storeService.dispatch(setStandard(files))
}
this.loadInitialFileService.loadFileOrSample()
}
}

Expand Down

0 comments on commit 139cb2e

Please sign in to comment.