import { TestBed } from "@angular/core/testing" ; import { ChromeDataStore } from "./chrome-data-store.service" ; import { StorageLocationModel } from "../storage-location.model" ; import * as _ from "lodash" ; import { AppUsage } from "../../models/app-usage.model" ; import { AppUsageDetails } from "../../models/app-usage-details.model" ; describe ( "ChromeDataStore" , ( ) => { class DreamInception { dream0 : { dream1 : { dream2 : string ; } ; } ; } class Foo extends Object { bar : string | boolean ; nested ? : DreamInception ; } let chromeDataStore ; let storageLocation ; let browserStorageLocalSpy ; let browserStorageErrorSpy ; const CHROME_QUOTA_BYTES = 1024 ; const CHROME_BYTES_IN_USE = 512 ; let CHROME_STORAGE_STUB = { } ; let DEFAULT_FOO ; const chromeStorageBehaviour = ( ) => { return { get : ( query , callback ) => { if ( _ . isString ( query ) ) { const response = { } ; if ( CHROME_STORAGE_STUB [ query ] ) { response [ query ] = CHROME_STORAGE_STUB [ query ] ; } callback ( response ) ; } else if ( _ . isArray ( query ) && query . length > 0 ) { const response = { } ; _ . forEach ( query , ( key ) => { if ( ! _ . isUndefined ( CHROME_STORAGE_STUB [ key ] ) ) { response [ key ] = CHROME_STORAGE_STUB [ key ] ; } } ) ; callback ( response ) ; } else if ( _ . isObject ( query ) ) { const response = _ . cloneDeep ( query ) ; _ . forEach ( _ . keys ( response ) , ( key ) => { if ( ! _ . isUndefined ( CHROME_STORAGE_STUB [ key ] ) ) { response [ key ] = CHROME_STORAGE_STUB [ key ] ; } } ) ; callback ( response ) ; } else if ( _ . isUndefined ( query ) || _ . isNull ( query ) ) { callback ( CHROME_STORAGE_STUB ) ; } else { callback ( { } ) ; } } , set : ( object , callback ) => { if ( ! _ . isObject ( object ) ) { throw new Error ( "Must be an object" ) ; } _ . forEach ( _ . keys ( object ) , ( key ) => { CHROME_STORAGE_STUB [ key ] = object [ key ] ; } ) ; callback ( ) ; } , remove : ( key , callback ) => { delete CHROME_STORAGE_STUB [ key ] ; callback ( ) ; } , clear : ( callback ) => { CHROME_STORAGE_STUB = { } ; callback ( ) ; } , QUOTA_BYTES : CHROME_QUOTA_BYTES , getBytesInUse : ( callback ) => { callback ( CHROME_BYTES_IN_USE ) ; } } ; } ; beforeEach ( ( done ) => { storageLocation = { key : "foo" } ; TestBed . configureTestingModule ( { providers : [ ChromeDataStore ] } ) ; chromeDataStore = TestBed . get ( ChromeDataStore ) ; browserStorageLocalSpy = spyOn ( chromeDataStore , "chromeLocalStorageArea" ) . and . callFake ( chromeStorageBehaviour ) ; CHROME_STORAGE_STUB = { } ; DEFAULT_FOO = { bar : "I am the default one" } ; browserStorageErrorSpy = spyOn ( chromeDataStore , "getLastError" ) ; browserStorageErrorSpy . and . returnValue ( null ) ; done ( ) ; } ) ; it ( "should be created" , ( done ) => { expect ( chromeDataStore ) . toBeTruthy ( ) ; done ( ) ; } ) ; it ( "should fetch data" , ( done ) => { const expectedData = [ { bar : "john doe" } ] ; CHROME_STORAGE_STUB [ storageLocation . key ] = expectedData ; const promise = < Promise < Foo [ ] > > chromeDataStore . fetch ( storageLocation , null , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( expectedData ) ; expect ( browserStorageLocalSpy ) . toHaveBeenCalledTimes ( 1 ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should fetch empty data (vector)" , ( done ) => { CHROME_STORAGE_STUB = { } ; const defaultValue = [ ] ; const promise = < Promise < Foo [ ] > > chromeDataStore . fetch ( storageLocation , null , defaultValue ) ; promise . then ( ( result ) => { expect ( result ) . toEqual ( defaultValue ) ; expect ( browserStorageLocalSpy ) . toHaveBeenCalledTimes ( 1 ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should fetch empty data (object))" , ( done ) => { CHROME_STORAGE_STUB = { } ; const defaultValue = { bar : "john doe" } ; const promise = < Promise < Foo > > chromeDataStore . fetch ( storageLocation , null , defaultValue ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( defaultValue ) ; expect ( browserStorageLocalSpy ) . toHaveBeenCalledTimes ( 1 ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should fetch all chrome storage data (no key provided)" , ( done ) => { const expectedData = { bar : "john doe" } ; CHROME_STORAGE_STUB = expectedData ; storageLocation = new StorageLocationModel ( ) ; const promise = < Promise < Foo > > chromeDataStore . fetch ( storageLocation , null , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( expectedData ) ; expect ( browserStorageLocalSpy ) . toHaveBeenCalledTimes ( 1 ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should fetch default value when storage is empty (and no key provided)" , ( done ) => { const defaultValue = { bar : "Default Bar" } ; storageLocation = new StorageLocationModel ( ) ; const promise = < Promise < Foo > > chromeDataStore . fetch ( storageLocation , null , defaultValue ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( defaultValue ) ; expect ( browserStorageLocalSpy ) . toHaveBeenCalledTimes ( 1 ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should save data" , ( done ) => { CHROME_STORAGE_STUB [ storageLocation . key ] = null ; const toBeSaved = [ { bar : "john doe" } ] ; const expectedChromeStorageStubState = { foo : toBeSaved } ; const promise = < Promise < Foo [ ] > > chromeDataStore . save ( storageLocation , toBeSaved , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( toBeSaved ) ; expect ( CHROME_STORAGE_STUB ) . toEqual ( expectedChromeStorageStubState ) ; expect ( browserStorageLocalSpy ) . toHaveBeenCalledTimes ( 2 ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should save all chrome storage data (no key provided)" , ( done ) => { const toBeSaved = { bar : "john doe" } ; storageLocation = new StorageLocationModel ( ) ; const promise = < Promise < Foo > > chromeDataStore . save ( storageLocation , toBeSaved , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( toBeSaved ) ; expect ( CHROME_STORAGE_STUB ) . toEqual ( toBeSaved ) ; expect ( browserStorageLocalSpy ) . toHaveBeenCalledTimes ( 2 ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should reject save data" , ( done ) => { const saveData = [ { bar : "john doe" } ] ; const expectedChromeError = { message : "Houston we have a problem" } ; browserStorageErrorSpy . and . returnValue ( expectedChromeError ) ; const promise = < Promise < Foo [ ] > > chromeDataStore . save ( storageLocation , saveData , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . toBeNull ( ) ; expect ( false ) . toBeTruthy ( "Whoops! I should not be here!" ) ; done ( ) ; } , error => { expect ( error ) . toEqual ( expectedChromeError . message ) ; done ( ) ; } ) ; } ) ; it ( "should save property of a Foo (string)" , ( done ) => { const currentFoo = { bar : "sheldon" } ; CHROME_STORAGE_STUB [ storageLocation . key ] = currentFoo ; const relativePath = "bar" ; const newValue = "Bazinga!" ; const expectedChromeStorageStubState = _ . cloneDeep ( CHROME_STORAGE_STUB ) as { foo : Foo ; } ; expectedChromeStorageStubState . foo . bar = newValue ; const promise = chromeDataStore . upsertProperty < string > ( storageLocation , relativePath , newValue , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( expectedChromeStorageStubState . foo ) ; expect ( CHROME_STORAGE_STUB ) . toEqual ( expectedChromeStorageStubState ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should save property of a Foo (boolean)" , ( done ) => { const currentFoo = { bar : false } ; CHROME_STORAGE_STUB [ storageLocation . key ] = currentFoo ; const relativePath = "bar" ; const newValue = true ; const expectedChromeStorageStubState = _ . cloneDeep ( CHROME_STORAGE_STUB ) as { foo : Foo ; } ; expectedChromeStorageStubState . foo . bar = newValue ; const promise = chromeDataStore . upsertProperty < boolean > ( storageLocation , relativePath , newValue , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( expectedChromeStorageStubState . foo ) ; expect ( CHROME_STORAGE_STUB ) . toEqual ( expectedChromeStorageStubState ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should save nested property" , ( done ) => { const currentFoo = { bar : "sheldon" , nested : { dream0 : { dream1 : { dream2 : "Wahoo" } } } } ; CHROME_STORAGE_STUB [ storageLocation . key ] = currentFoo ; const relativePath = [ "nested" , "dream0" , "dream1" , "dream2" ] ; const newValue = "Bazinga!" ; const expectedChromeStorageStubState = _ . cloneDeep ( CHROME_STORAGE_STUB ) as { foo : Foo ; } ; expectedChromeStorageStubState . foo . nested . dream0 . dream1 . dream2 = newValue ; const promise = chromeDataStore . upsertProperty < string > ( storageLocation , relativePath , newValue , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( expectedChromeStorageStubState . foo ) ; expect ( CHROME_STORAGE_STUB ) . toEqual ( expectedChromeStorageStubState ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should save nested property (no key provided)" , ( done ) => { storageLocation = new StorageLocationModel ( ) ; const currentFoo = { bar : "sheldon" , nested : { dream0 : { dream1 : { dream2 : "Wahoo" } } } } ; CHROME_STORAGE_STUB = currentFoo ; const relativePath = [ "nested" , "dream0" , "dream1" , "dream2" ] ; const newValue = "Bazinga!" ; const expectedChromeStorageStubState = _ . cloneDeep ( CHROME_STORAGE_STUB ) as Foo ; expectedChromeStorageStubState . nested . dream0 . dream1 . dream2 = newValue ; const promise = chromeDataStore . upsertProperty < string > ( storageLocation , relativePath , newValue , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( expectedChromeStorageStubState ) ; expect ( CHROME_STORAGE_STUB ) . toEqual ( expectedChromeStorageStubState ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should reject save property with Foo[]" , ( done ) => { const currentFooList = [ { bar : "john doe" , nested : { dream0 : { dream1 : { dream2 : "Wahoo" } } } } ] ; CHROME_STORAGE_STUB [ storageLocation . key ] = currentFooList ; const relativePath = [ "nested" , "dream0" , "dream1" , "dream2" ] ; const newValue = "Bazinga!" ; const promise = chromeDataStore . upsertProperty < string > ( storageLocation , relativePath , newValue , DEFAULT_FOO ) ; promise . then ( ( result ) => { expect ( result ) . toBeNull ( ) ; expect ( false ) . toBeTruthy ( "Whoops! I should not be here!" ) ; done ( ) ; } , error => { expect ( error ) . toEqual ( "Cannot save property to a storage type 'vector'" ) ; done ( ) ; } ) ; } ) ; it ( "should clear data" , ( done ) => { const otherData = { otherData : "I am another data" } ; CHROME_STORAGE_STUB = otherData ; CHROME_STORAGE_STUB [ storageLocation . key ] = "FakeData" ; const promise = chromeDataStore . clear ( storageLocation ) ; promise . then ( ( ) => { expect ( CHROME_STORAGE_STUB ) . toEqual ( otherData ) ; expect ( browserStorageLocalSpy ) . toHaveBeenCalledTimes ( 1 ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should clear all chrome storage data (no key provided)" , ( done ) => { CHROME_STORAGE_STUB = { nothing : "everything" } ; storageLocation = new StorageLocationModel ( ) ; const expectedResult = < Foo > { } ; const promise = chromeDataStore . clear ( storageLocation ) ; promise . then ( ( ) => { expect ( CHROME_STORAGE_STUB ) . toEqual ( expectedResult ) ; expect ( browserStorageLocalSpy ) . toHaveBeenCalledTimes ( 1 ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; it ( "should reject clear data" , ( done ) => { const expectedChromeError = { message : "Houston we have a problem" } ; browserStorageErrorSpy . and . returnValue ( expectedChromeError ) ; const promise = chromeDataStore . clear ( storageLocation ) ; promise . then ( ( ) => { expect ( false ) . toBeTruthy ( "Whoops! I should not be here!" ) ; done ( ) ; } , error => { expect ( error ) . toEqual ( expectedChromeError . message ) ; done ( ) ; } ) ; } ) ; it ( "should provide AppUsageDetails" , ( done ) => { const appUsage = new AppUsage ( CHROME_BYTES_IN_USE , CHROME_QUOTA_BYTES ) ; const expectedAppUsageDetails = new AppUsageDetails ( appUsage , CHROME_BYTES_IN_USE / ( 1024 * 1024 ) , CHROME_BYTES_IN_USE / CHROME_QUOTA_BYTES * 100 ) ; const promise = chromeDataStore . getAppUsageDetails ( ) ; promise . then ( ( result ) => { expect ( result ) . not . toBeNull ( ) ; expect ( result ) . toEqual ( expectedAppUsageDetails ) ; done ( ) ; } , error => { expect ( error ) . toBeNull ( ) ; done ( ) ; } ) ; } ) ; } ) ;