diff --git a/.github/workflows/serve-install.yml b/.github/workflows/serve-install.yml index 288c1c8cb..a5e634821 100644 --- a/.github/workflows/serve-install.yml +++ b/.github/workflows/serve-install.yml @@ -56,6 +56,11 @@ jobs: echo "ionic version" npx ionic --version + - name: Run Jest tests + shell: bash -l {0} + run: | + npx jest + # TODO: figure out how to check that a server started correctly # - name: Try starting it # run: npx run serve diff --git a/jest.config.json b/jest.config.json new file mode 100644 index 000000000..78dc839b4 --- /dev/null +++ b/jest.config.json @@ -0,0 +1,15 @@ +{ + "testPathIgnorePatterns": [ + "/node_modules/", + "/platforms/", + "/plugins/", + "/lib/", + "/manual_lib/" + ], + "transform": { + "^.+\\.(ts|tsx|js|jsx)$": "ts-jest" + }, + "moduleNameMapper": { + "^react-native$": "react-native-web" + } +} diff --git a/package.serve.json b/package.serve.json index da3b60b89..a342a73de 100644 --- a/package.serve.json +++ b/package.serve.json @@ -10,7 +10,8 @@ "scripts": { "setup-serve": "./bin/download_settings_controls.js && ./bin/setup_autodeploy.js", "serve": "webpack --config webpack.dev.js && concurrently -k \"phonegap --verbose serve\" \"webpack --config webpack.dev.js --watch\"", - "serve-only": "phonegap --verbose serve" + "serve-only": "phonegap --verbose serve", + "test": "npx jest" }, "devDependencies": { "@babel/core": "^7.21.3", @@ -30,11 +31,13 @@ "exports-loader": "^4.0.0", "expose-loader": "^4.1.0", "file-loader": "^6.2.0", + "jest": "^29.7.0", "phonegap": "9.0.0+cordova.9.0.0", "process": "^0.11.10", "sass": "^1.62.1", "sass-loader": "^13.3.1", "style-loader": "^3.3.3", + "ts-jest": "^29.1.1", "ts-loader": "^9.4.2", "typescript": "^5.0.3", "url-loader": "^4.1.1", @@ -45,6 +48,7 @@ "@react-navigation/native": "^6.1.7", "@react-navigation/stack": "^6.3.17", "@shopify/flash-list": "^1.3.1", + "@types/jest": "^29.5.5", "angular": "1.6.7", "angular-animate": "1.6.7", "angular-local-storage": "^0.7.1", diff --git a/www/__tests__/diaryHelper.test.ts b/www/__tests__/diaryHelper.test.ts new file mode 100644 index 000000000..429fbd08e --- /dev/null +++ b/www/__tests__/diaryHelper.test.ts @@ -0,0 +1,65 @@ +import { getFormattedSectionProperties, getFormattedDate, motionTypeOf, isMultiDay, getFormattedDateAbbr, getFormattedTimeRange, getPercentages } from "../js/diary/diaryHelper"; +import { useImperialConfig } from "../js/config/useImperialConfig"; + +it('returns a formatted date', () => { + expect(getFormattedDate("2023-09-18T00:00:00-07:00")).toBe("Mon September 18, 2023"); + expect(getFormattedDate("")).toBeUndefined(); + expect(getFormattedDate("2023-09-18T00:00:00-07:00", "2023-09-21T00:00:00-07:00")).toBe("Mon September 18, 2023 - Thu September 21, 2023"); +}); + +it('returns an abbreviated formatted date', () => { + expect(getFormattedDateAbbr("2023-09-18T00:00:00-07:00")).toBe("Mon, Sep 18"); + expect(getFormattedDateAbbr("")).toBeUndefined(); + expect(getFormattedDateAbbr("2023-09-18T00:00:00-07:00", "2023-09-21T00:00:00-07:00")).toBe("Mon, Sep 18 - Thu, Sep 21"); +}); + +it('returns a human readable time range', () => { + expect(getFormattedTimeRange("2023-09-18T00:00:00-07:00", "2023-09-18T00:00:00-09:20")).toBe("2 hours"); + expect(getFormattedTimeRange("2023-09-18T00:00:00-07:00", "2023-09-18T00:00:00-09:30")).toBe("3 hours"); + expect(getFormattedTimeRange("", "2023-09-18T00:00:00-09:30")).toBeFalsy(); +}); + +it("returns a MotionType object", () => { + expect(motionTypeOf("WALKING")).toEqual({ name: "WALKING", icon: "walk", color: '#0068a5' }); + expect(motionTypeOf("MotionTypes.WALKING")).toEqual({ name: "WALKING", icon: "walk", color: '#0068a5' }); + expect(motionTypeOf("I made this type up")).toEqual({ name: "UNKNOWN", icon: "help", color: '#484848'}); +}); + +it('returns true/false is multi day', () => { + expect(isMultiDay("2023-09-18T00:00:00-07:00", "2023-09-19T00:00:00-07:00")).toBeTruthy(); + expect(isMultiDay("2023-09-18T00:00:00-07:00", "2023-09-18T00:00:00-09:00")).toBeFalsy(); + expect(isMultiDay("", "2023-09-18T00:00:00-09:00")).toBeFalsy(); +}); + +//created a fake trip with relevant sections by examining log statements +let myFakeTrip = {sections: [ + { "sensed_mode_str": "BICYCLING", "distance": 6013.73657416706 }, + { "sensed_mode_str": "WALKING", "distance": 715.3078629361006 } +]}; +let myFakeTrip2 = {sections: [ + { "sensed_mode_str": "BICYCLING", "distance": 6013.73657416706 }, + { "sensed_mode_str": "BICYCLING", "distance": 715.3078629361006 } +]}; + +let myFakePcts = [ + { mode: "BICYCLING", + icon: "bike", + color: '#007e46', + pct: 89 }, + { mode: "WALKING", + icon: "walk", + color: '#0068a5', + pct: 11 }]; + +let myFakePcts2 = [ + { mode: "BICYCLING", + icon: "bike", + color: '#007e46', + pct: 100 }]; + +it('returns the percetnages by mode for a trip', () => { + expect(getPercentages(myFakeTrip)).toEqual(myFakePcts); + expect(getPercentages(myFakeTrip2)).toEqual(myFakePcts2); + expect(getPercentages({})).toEqual({}); +}) + diff --git a/www/js/diary/diaryHelper.ts b/www/js/diary/diaryHelper.ts index 88dd9baa5..942268ec7 100644 --- a/www/js/diary/diaryHelper.ts +++ b/www/js/diary/diaryHelper.ts @@ -48,7 +48,7 @@ type MotionTypeKey = keyof typeof MotionTypes; export function motionTypeOf(motionName: MotionTypeKey | `MotionTypes.${MotionTypeKey}`) { let key = ('' + motionName).toUpperCase(); key = key.split(".").pop(); // if "MotionTypes.WALKING", then just take "WALKING" - return MotionTypes[motionName] || MotionTypes.UNKNOWN; + return MotionTypes[key] || MotionTypes.UNKNOWN; } /**