diff --git a/src/components/Header.vue b/src/components/Header.vue
index 9a4a7827..c7f37db3 100644
--- a/src/components/Header.vue
+++ b/src/components/Header.vue
@@ -59,6 +59,9 @@ div(:class="{'fixed-top-padding': fixedTopMenu}")
b-dropdown-item(to="/search")
icon(name="search")
| Search
+ b-dropdown-item(to="/work-report")
+ icon(name="briefcase")
+ | Work Report
b-dropdown-item(to="/trends" v-if="devmode")
icon(name="chart-line")
| Trends
@@ -98,6 +101,7 @@ div(:class="{'fixed-top-padding': fixedTopMenu}")
+
+
diff --git a/test/unit/route.test.js b/test/unit/route.test.js
new file mode 100644
index 00000000..b9360c01
--- /dev/null
+++ b/test/unit/route.test.js
@@ -0,0 +1,24 @@
+import router from '~/route';
+
+describe('router', () => {
+ test('root redirect uses localStorage landingpage when set', () => {
+ const rootRoute = router.options.routes.find(route => route.path === '/');
+
+ localStorage.landingpage = '/work-report';
+ expect(rootRoute.redirect({})).toBe('/work-report');
+ });
+
+ test('root redirect falls back to /home when localStorage landingpage is missing', () => {
+ const rootRoute = router.options.routes.find(route => route.path === '/');
+
+ delete localStorage.landingpage;
+ expect(rootRoute.redirect({})).toBe('/home');
+ });
+
+ test('includes the work report route', () => {
+ const workReportRoute = router.options.routes.find(route => route.path === '/work-report');
+
+ expect(workReportRoute).toBeTruthy();
+ expect(typeof workReportRoute.component).toBe('function');
+ });
+});
diff --git a/test/unit/workReport.test.node.ts b/test/unit/workReport.test.node.ts
new file mode 100644
index 00000000..a79e4203
--- /dev/null
+++ b/test/unit/workReport.test.node.ts
@@ -0,0 +1,70 @@
+import {
+ getSupportedWorkReportHosts,
+ getUnsupportedWorkReportHosts,
+ getWorkReportHostOptions,
+} from '~/util/workReport';
+
+const buckets = [
+ {
+ id: 'aw-watcher-window_laptop',
+ hostname: 'laptop',
+ device_id: 'laptop',
+ type: 'currentwindow',
+ data: {},
+ },
+ {
+ id: 'aw-watcher-afk_laptop',
+ hostname: 'laptop',
+ device_id: 'laptop',
+ type: 'afkstatus',
+ data: {},
+ },
+ {
+ id: 'aw-watcher-window_phone',
+ hostname: 'phone',
+ device_id: 'phone',
+ type: 'currentwindow',
+ data: {},
+ },
+];
+
+describe('workReport host helpers', () => {
+ test('getWorkReportHostOptions disables hosts without AFK buckets', () => {
+ expect(getWorkReportHostOptions(buckets as any)).toEqual([
+ { value: 'laptop', text: 'laptop', disabled: false },
+ { value: 'phone', text: 'phone (requires aw-watcher-afk)', disabled: true },
+ ]);
+ });
+
+ test('getUnsupportedWorkReportHosts returns selected hosts missing AFK buckets', () => {
+ expect(getUnsupportedWorkReportHosts(['laptop', 'phone'], buckets as any)).toEqual(['phone']);
+ });
+
+ test('getSupportedWorkReportHosts returns only hosts with AFK buckets', () => {
+ expect(getSupportedWorkReportHosts(['laptop', 'phone'], buckets as any)).toEqual(['laptop']);
+ });
+
+ test('getSupportedWorkReportHosts preserves selected host order', () => {
+ const moreBuckets = [
+ ...buckets,
+ {
+ id: 'aw-watcher-window_desktop',
+ hostname: 'desktop',
+ device_id: 'desktop',
+ type: 'currentwindow',
+ data: {},
+ },
+ {
+ id: 'aw-watcher-afk_desktop',
+ hostname: 'desktop',
+ device_id: 'desktop',
+ type: 'afkstatus',
+ data: {},
+ },
+ ];
+
+ expect(getSupportedWorkReportHosts(['desktop', 'phone', 'laptop'], moreBuckets as any)).toEqual(
+ ['desktop', 'laptop']
+ );
+ });
+});