Repository: temporalio/ui
Pull request #140 (2021-11-29 19:54:20)

Remove sidebar
Removes the sidebar from the UI.


Patch:
diff --git a/package-lock.json b/package-lock.json
index 7e43a7ad..d668791a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,7 +8,6 @@
       "name": "temporal-ui",
       "version": "0.0.1",
       "dependencies": {
-        "base-64": "^1.0.0",
         "date-fns": "^2.23.0",
         "json-beautify": "^1.1.1",
         "svelte-hero-icons": "^3.0.0",
@@ -2142,11 +2141,6 @@
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
-    "node_modules/base-64": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
-      "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="
-    },
     "node_modules/bcrypt-pbkdf": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@@ -10851,11 +10845,6 @@
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
-    "base-64": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
-      "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="
-    },
     "bcrypt-pbkdf": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
diff --git a/package.json b/package.json
index 85a665fa..33c240f9 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,6 @@
     "cypress:open": "TEMPORAL_API_HOST=http://localhost:3000 cypress open"
   },
   "dependencies": {
-    "base-64": "^1.0.0",
     "date-fns": "^2.23.0",
     "json-beautify": "^1.1.1",
     "svelte-hero-icons": "^3.0.0",
diff --git a/src/lib/components/outside-of-dropdown.svelte b/src/lib/components/outside-of-dropdown.svelte
index de38a8db..2e495314 100644
--- a/src/lib/components/outside-of-dropdown.svelte
+++ b/src/lib/components/outside-of-dropdown.svelte
@@ -1,10 +1,10 @@
 <script lang="ts">
-  // When using outside of dropdown component binded variable must be named showDropdown
+  // When using outside of dropdown component bound variable must be named showDropdown
   export let showDropdown: boolean;
   let node: Node;
 
-  const windowClick = (event) => {
-    if (!node.contains(event.target)) {
+  const windowClick = (event: MouseEvent) => {
+    if (!node.contains(event.target as Node)) {
       showDropdown = false;
     }
   };
diff --git a/src/lib/models/workflow-execution.ts b/src/lib/models/workflow-execution.ts
index ee767ce0..fb5d9f2d 100644
--- a/src/lib/models/workflow-execution.ts
+++ b/src/lib/models/workflow-execution.ts
@@ -19,7 +19,6 @@ export type WorkflowExecution = {
   historyEvents: Long;
   pendingActivities: PendingActivityInfo[];
   url: string;
-  toggleUrl: (isFullScreen: boolean) => string;
 };
 
 type WorkflowExecutionAPIResponse = Optional<
@@ -41,12 +40,6 @@ export const toWorkflowExecution = (
   const taskQueue = response?.executionConfig?.taskQueue?.name;
   const pendingActivities = response.pendingActivities || [];
 
-  const toggleUrl = (isFullScreen: boolean): string => {
-    return `${url}?${new URLSearchParams({
-      fullScreen: (!isFullScreen).toString(),
-    })}`;
-  };
-
   return {
     name,
     id,
@@ -56,7 +49,6 @@ export const toWorkflowExecution = (
     status,
     historyEvents,
     url,
-    toggleUrl,
     taskQueue,
     pendingActivities,
   };
diff --git a/src/lib/stores/full-screen.ts b/src/lib/stores/full-screen.ts
deleted file mode 100644
index a22328d9..00000000
--- a/src/lib/stores/full-screen.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { page } from '$app/stores';
-import { derived } from 'svelte/store';
-import UrlPattern from 'url-pattern';
-
-const pattern = new UrlPattern(
-  '/namespaces/:namespace/workflows/:executionId/:runId/:view(/*)',
-);
-
-export const isFullScreen = derived(page, ($page) => {
-  return !!pattern.match($page.path);
-});
diff --git a/src/lib/stores/sorted.ts b/src/lib/stores/sorted.ts
deleted file mode 100644
index c5f3d990..00000000
--- a/src/lib/stores/sorted.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import type { Readable, Writable } from 'svelte/store';
-import { derived, writable } from 'svelte/store';
-
-type Order = 'ascending' | 'descending';
-
-export type SortedStore<T> = Readable<T[]> & {
-  property: Writable<keyof T>;
-  order: Writable<Order>;
-};
-
-const isNumber = (x: unknown): x is number => typeof x === 'number';
-const isString = (x: unknown): x is string => typeof x === 'string';
-
-const getValues = <T>(
-  values: T[],
-  property: keyof T,
-  order: Order,
-): [T[keyof T], T[keyof T]] => {
-  const [x, y] = values.map((value) => value[property]);
-  if (order === 'ascending') return [x, y];
-  if (order === 'descending') return [y, x];
-};
-
-const compare =
-  <T>(property: keyof T, order: Order) =>
-  (a: T, b: T): number => {
-    const [x, y] = getValues([a, b], property, order);
-
-    if (isNumber(x) && isNumber(y)) {
-      return x - y;
-    }
-
-    if (isString(x) && isString(y)) {
-      if (x > y) return 1;
-      if (x < y) return -1;
-      return 0;
-    }
-  };
-
-export const sorted = <T>(
-  store: Readable<T[]>,
-  prop: keyof T = null,
-  defaultOrder: Order = 'ascending',
-): SortedStore<T> => {
-  const order = writable<Order>(defaultOrder);
-  const property = writable<keyof T>(prop);
-
-  const sortedStore = derived(
-    [store, property, order],
-    ([$store, $property, $order]) => {
-      return $store.sort(compare($property, $order));
-    },
-  );
-
-  return {
-    ...sortedStore,
-    order,
-    property,
-  };
-};
diff --git a/src/lib/utilities/decode-payload.ts b/src/lib/utilities/decode-payload.ts
index fcb6d95b..b80c9834 100644
--- a/src/lib/utilities/decode-payload.ts
+++ b/src/lib/utilities/decode-payload.ts
@@ -2,7 +2,6 @@ import type {
   WorkflowExecutionStartedEventAttributes,
   WorkflowExecutionCompletedEventAttributes,
 } from '$types';
-import base64 from 'base-64';
 
 type DecodeEvent = BaseEvent & {
   workflowExecutionStartedEventAttributes?: WorkflowExecutionStartedEventAttributes;
@@ -17,7 +16,7 @@ export const convertPayloadToJson = (event: DecodeEvent): string => {
     if (input === null) return 'null';
     if (input) {
       const [data] = input.payloads.map((payload) => payload.data);
-      return base64.decode(String(data));
+      return window.atob(String(data));
     }
   }
 
@@ -26,7 +25,7 @@ export const convertPayloadToJson = (event: DecodeEvent): string => {
     if (result === null) return 'null';
     if (result) {
       const [data] = result.payloads.map((payload) => payload.data);
-      return base64.decode(String(data));
+      return window.atob(String(data));
     }
   }
 };
diff --git a/src/lib/utilities/get-workflow-execution-url.ts b/src/lib/utilities/get-workflow-execution-url.ts
index 810653c3..8102a9cc 100644
--- a/src/lib/utilities/get-workflow-execution-url.ts
+++ b/src/lib/utilities/get-workflow-execution-url.ts
@@ -3,9 +3,11 @@ import type { WorkflowExecution } from '$lib/models/workflow-execution';
 import type { URLSearchParamLike } from './url-search-params';
 import { mergeSearchParams, toSearchParams } from './url-search-params';
 
+type WorkflowIdentifier = Pick<WorkflowExecution, 'id' | 'runId'>;
+
 export const getWorkflowExecutionUrl = (
   namespace: string,
-  workflow: WorkflowExecution,
+  workflow: WorkflowIdentifier,
   query?: URLSearchParamLike,
   queryOverrides?: URLSearchParamLike,
 ): string => {
diff --git a/src/lib/utilities/path-matches.ts b/src/lib/utilities/path-matches.ts
index 854c3145..d3fbea8f 100644
--- a/src/lib/utilities/path-matches.ts
+++ b/src/lib/utilities/path-matches.ts
@@ -1,13 +1,21 @@
 const dropQueryParameters = (url: string) => {
-  const queryParameterIndex = url.indexOf('?');
+  const queryParameterIndex = url?.indexOf('?');
   if (queryParameterIndex > -1) return url.slice(0, queryParameterIndex);
   return url;
 };
 
-export const pathMatches = (first: string, second: string): boolean => {
+export const pathMatches = (
+  first: string,
+  second: string,
+  exactMatch = false,
+): boolean => {
   const firstSegments = dropQueryParameters(first).split('/');
   const secondSegments = dropQueryParameters(second).split('/');
 
+  if (exactMatch && firstSegments.length !== secondSegments.length) {
+    return false;
+  }
+
   for (let index = 0; index < firstSegments.length; index++) {
     const firstSegment = firstSegments[index];
     const secondSegment = secondSegments[index];
diff --git a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/__layout.svelte b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/__layout.svelte
index 651fddfc..f712d020 100644
--- a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/__layout.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/__layout.svelte
@@ -15,12 +15,8 @@
 </script>
 
 <script lang="ts">
-  import { fly } from 'svelte/transition';
-  import { isFullScreen } from '$lib/stores/full-screen';
-
   import Header from './_header.svelte';
   import { getWorkflow } from '$lib/stores/workflow';
-  import { setContext } from 'svelte';
 
   export let executionId: string;
   export let runId: string;
@@ -29,35 +25,11 @@
   $: store = getWorkflow({ executionId, runId, namespace });
   $: workflow = $store.data;
   $: loading = $store.loading;
-
-  setContext('workflow', store);
 </script>
 
-<section
-  class="border-l-2 h-screen"
-  class:full={$isFullScreen}
-  class:sidebar={!$isFullScreen}
-  in:fly={{ x: 500, duration: 350 }}
-  out:fly={{ x: 500 }}
->
+<section class="border-l-2 h-screen">
   <main class="w-full">
     <Header {workflow} {loading} />
     <slot />
   </main>
 </section>
-
-<style type="postcss">
-  .full {
-    @apply w-full;
-  }
-
-  .sidebar {
-    background-color: white;
-    width: 600px;
-    left: calc(100% - 600px);
-    position: absolute;
-    overflow-y: scroll;
-    box-shadow: -2px 14px 20px 0px rgb(0 0 0 / 20%);
-    z-index: 2;
-  }
-</style>
diff --git a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/_header.svelte b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/_header.svelte
index 309638d4..4aee11ad 100644
--- a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/_header.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/_header.svelte
@@ -1,11 +1,4 @@
 <script lang="ts">
-  import Icon from 'svelte-hero-icons/Icon.svelte';
-  import { X, ArrowLeft, ArrowRight } from 'svelte-hero-icons';
-
-  import { namespace } from '$lib/stores/namespace';
-  import { isFullScreen } from '$lib/stores/full-screen';
-  import { getWorkflowExecutionUrl } from '$lib/utilities/get-workflow-execution-url';
-
   import type { WorkflowExecution } from '$lib/models/workflow-execution';
 
   import Tabs from './_tabs.svelte';
@@ -13,26 +6,9 @@
 
   export let workflow: WorkflowExecution;
   export let loading: boolean;
-
-  $: workflowUrl = getWorkflowExecutionUrl($namespace, workflow);
-  $: status = workflow?.status;
 </script>
 
 <header class="flex flex-col justify-between">
-  <div class="py-4 px-2 bg-gray-50 border-b-2 border-gray-100 flex justify-end">
-    {#if $isFullScreen}
-      <a href={workflowUrl}>
-        <Icon src={ArrowRight} class="w-8 h-8 text-gray-500" />
-      </a>
-    {:else}
-      <a href={workflowUrl + '/summary'}>
-        <Icon src={ArrowLeft} class="w-8 h-8 text-gray-500" />
-      </a>
-    {/if}
-    <a href={`/namespaces/${$namespace}/workflows`}>
-      <Icon src={X} class="w-8 h-8 text-gray-500" />
-    </a>
-  </div>
   {#if !loading}
     <main class="px-6">
       <div class="flex m-0 mt-6 justify-between items-center">
@@ -40,7 +16,7 @@
           {workflow.name}
         </h1>
         <span class="inline">
-          <WorkflowStatus {status} />
+          <WorkflowStatus status={workflow?.status} />
         </span>
       </div>
       <p class="text-gray-500 text-xs">
@@ -53,5 +29,5 @@
       </p>
     </main>
   {/if}
-  <Tabs {workflow} />
+  <Tabs />
 </header>
diff --git a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/_tabs.svelte b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/_tabs.svelte
index cf1876d5..8607fde5 100644
--- a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/_tabs.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/_tabs.svelte
@@ -1,22 +1,21 @@
 <script lang="ts">
-  import { namespace } from '$lib/stores/namespace';
   import { page } from '$app/stores';
 
-  import type { WorkflowExecution } from '$lib/models/workflow-execution';
-
   import { getWorkflowExecutionUrl } from '$lib/utilities/get-workflow-execution-url';
   import { pathMatches } from '$lib/utilities/path-matches';
 
-  export let workflow: WorkflowExecution;
+  const { namespace, workflow: id, run: runId } = $page.params;
 
-  $: workflowUrl = getWorkflowExecutionUrl($namespace, workflow);
-  $: summaryUrl = workflowUrl + '/summary';
-  $: eventsUrl = workflowUrl + '/events';
-  $: activitiesUrl = workflowUrl + '/activities';
+  $: workflowUrl = getWorkflowExecutionUrl(namespace, { id, runId });
+  $: eventsUrl = `${workflowUrl}/events`;
+  $: activitiesUrl = `${workflowUrl}/activities`;
 </script>
 
 <nav class="mt-6 border-b-2 px-2 flex">
-  <a class:active={pathMatches(summaryUrl, $page.path)} href={summaryUrl}>
+  <a
+    class:active={pathMatches(workflowUrl, $page.path, true)}
+    href={workflowUrl}
+  >
     Summary
   </a>
   <a class:active={pathMatches(eventsUrl, $page.path)} href={eventsUrl}>
diff --git a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/index.svelte b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/index.svelte
index baa1447d..314afa81 100644
--- a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/index.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/index.svelte
@@ -15,16 +15,16 @@
 </script>
 
 <script lang="ts">
-  import { isFullScreen } from '$lib/stores/full-screen';
-
-  import CodeBlock from '$lib/components/code-block.svelte';
+  import { getWorkflow } from '$lib/stores/workflow';
+  import { createEventStore } from '$lib/stores/events';
+  import { getWorkflowStartedAndCompletedEvents } from '$lib/utilities/get-started-and-completed-events';
   import { getTaskQueueUrl } from '$lib/utilities/get-task-queue-url';
+
   import ExecutionInformation from './_execution-information.svelte';
   import TaskQueueInformation from './_task-queue-information.svelte';
   import PendingActivities from './_pending-activities.svelte';
-  import { getWorkflowStartedAndCompletedEvents } from '$lib/utilities/get-started-and-completed-events';
-  import { getWorkflow } from '$lib/stores/workflow';
-  import { createEventStore } from '$lib/stores/events';
+  import CodeBlock from '$lib/components/code-block.svelte';
+  import TerminateWorkflow from '$lib/components/terminate-workflow.svelte';
 
   export let executionId: string;
   export let runId: string;
@@ -38,27 +38,23 @@
   $: events = $eventStore.data;
   $: inputAndResults = getWorkflowStartedAndCompletedEvents(events);
   $: pendingActivities = workflow?.pendingActivities;
-  $: href = getTaskQueueUrl(namespace, workflow?.taskQueue);
+  $: taskQueue = workflow?.taskQueue;
+  $: historyEvents = workflow?.historyEvents;
+  $: href = getTaskQueueUrl(namespace, taskQueue);
 </script>
 
-<div class="execution-information px-6 py-6 flex flex-col">
+<div class="execution-information px-6 py-6">
   {#if !loading}
-    <div class={$isFullScreen ? 'w-1/3' : 'w-full'}>
+    <div class="w-full flex">
       <ExecutionInformation title="Start Time" value={workflow.startTime} />
       <ExecutionInformation title="End Time" value={workflow.endTime} />
-      <TaskQueueInformation
-        title="Task Queue"
-        value={workflow.taskQueue}
-        {href}
-      />
+      <TaskQueueInformation title="Task Queue" value={taskQueue} {href} />
+      <ExecutionInformation title="History Events" value={historyEvents} />
       <CodeBlock heading="Input" content={inputAndResults.input} />
       <CodeBlock heading="Result" content={inputAndResults.result} />
-      <ExecutionInformation
-        title="History Events"
-        value={workflow.historyEvents}
-      />
     </div>
-    <div class="pending-activities w-full">
+    <TerminateWorkflow {namespace} {workflow} refresh={workflowStore.refresh} />
+    <div class="flex w-full mt-4">
       <PendingActivities activities={pendingActivities} />
     </div>
   {/if}
diff --git a/src/routes/namespaces/[namespace]/workflows/__layout.svelte b/src/routes/namespaces/[namespace]/workflows/__layout.svelte
deleted file mode 100644
index d7ecf9f8..00000000
--- a/src/routes/namespaces/[namespace]/workflows/__layout.svelte
+++ /dev/null
@@ -1,52 +0,0 @@
-<script lang="ts">
-  import { isFullScreen } from '$lib/stores/full-screen';
-  import { namespace } from '$lib/stores/namespace';
-  import { createWorkflowStore } from '$lib/stores/workflows';
-
-  import WorkflowsSummaryTable from './_workflows-summary-table.svelte';
-  import WorkflowsSummaryRow from './_workflows-summary-row.svelte';
-  import WorkflowFilters from './_workflow-filters.svelte';
-  import WorkflowPagination from './_workflow-pagination.svelte';
-  import WorkflowsEmptyState from './_workflows-empty.svelte';
-  import Loading from '$lib/components/loading.svelte';
-
-  $: ({ workflows, loading, updating } = createWorkflowStore($namespace));
-
-  let timeFormat = 'relative';
-  let currentPage = 0;
-  let executionsPerPage = 50;
-
-  $: maximumPage = Math.ceil($workflows.length / executionsPerPage);
-
-  $: visibleWorkflows = $workflows.slice(
-    currentPage * executionsPerPage,
-    currentPage * executionsPerPage + executionsPerPage,
-  );
-
-  $: {
-    if (currentPage > maximumPage) currentPage = maximumPage - 1;
-  }
-</script>
-
-<section class="flex items-start">
-  {#if !$isFullScreen}
-    <div class="w-full h-screen overflow-scroll">
-      <header>
-        <WorkflowFilters bind:timeFormat />
-        <WorkflowPagination bind:currentPage {maximumPage} />
-      </header>
-      <Loading loading={$loading} updating={$updating} />
-      <WorkflowsSummaryTable>
-        <tbody slot="rows">
-          {#each visibleWorkflows as workflow}
-            <WorkflowsSummaryRow {workflow} {timeFormat} />
-          {:else}
-            <WorkflowsEmptyState />
-          {/each}
-        </tbody>
-      </WorkflowsSummaryTable>
-    </div>
-  {/if}
-
-  <slot />
-</section>
diff --git a/src/routes/namespaces/[namespace]/workflows/index.svelte b/src/routes/namespaces/[namespace]/workflows/index.svelte
index e69de29b..c6ab3447 100644
--- a/src/routes/namespaces/[namespace]/workflows/index.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/index.svelte
@@ -0,0 +1,47 @@
+<script lang="ts">
+  import { namespace } from '$lib/stores/namespace';
+  import { createWorkflowStore } from '$lib/stores/workflows';
+
+  import WorkflowsSummaryTable from './_workflows-summary-table.svelte';
+  import WorkflowsSummaryRow from './_workflows-summary-row.svelte';
+  import WorkflowFilters from './_workflow-filters.svelte';
+  import WorkflowPagination from './_workflow-pagination.svelte';
+  import WorkflowsEmptyState from './_workflows-empty.svelte';
+  import Loading from '$lib/components/loading.svelte';
+
+  $: ({ workflows, loading, updating } = createWorkflowStore($namespace));
+
+  let timeFormat = 'relative';
+  let currentPage = 0;
+  let executionsPerPage = 50;
+
+  $: maximumPage = Math.ceil($workflows.length / executionsPerPage);
+
+  $: visibleWorkflows = $workflows.slice(
+    currentPage * executionsPerPage,
+    currentPage * executionsPerPage + executionsPerPage,
+  );
+
+  $: {
+    if (currentPage > maximumPage) currentPage = maximumPage - 1;
+  }
+</script>
+
+<section class="flex items-start">
+  <div class="w-full h-screen overflow-scroll">
+    <header>
+      <WorkflowFilters bind:timeFormat />
+      <WorkflowPagination bind:currentPage {maximumPage} />
+    </header>
+    <Loading loading={$loading} updating={$updating} />
+    <WorkflowsSummaryTable>
+      <tbody slot="rows">
+        {#each visibleWorkflows as workflow}
+          <WorkflowsSummaryRow {workflow} {timeFormat} />
+        {:else}
+          <WorkflowsEmptyState />
+        {/each}
+      </tbody>
+    </WorkflowsSummaryTable>
+  </div>
+</section>


Test patch:
diff --git a/src/lib/stores/sorted.test.ts b/src/lib/stores/sorted.test.ts
deleted file mode 100644
index be927836..00000000
--- a/src/lib/stores/sorted.test.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { get, writable } from 'svelte/store';
-import { sorted } from './sorted';
-
-const musicians = [
-  { name: 'Jack White', birthYear: 1975 },
-  { name: 'J Mascis', birthYear: 1965 },
-  { name: 'Kurt Vile', birthYear: 1980 },
-  { name: 'Jessica Dobson', birthYear: 1984 },
-  { name: 'John Frusciante', birthYear: 1970 },
-  { name: 'Bartees Strange', birthYear: 1989 },
-];
-
-describe('sorted', () => {
-  it('should have a "subscribe" method', () => {
-    const store = writable(musicians);
-    const sortedStore = sorted(store);
-
-    expect(sortedStore.subscribe).toBeDefined();
-  });
-
-  it('should sort the items ascendingly by default', () => {
-    const store = writable(musicians);
-    const sortedStore = sorted(store, 'birthYear');
-    const [oldest] = get(sortedStore);
-
-    expect(oldest.name).toEqual('J Mascis');
-  });
-
-  it('should sort the items ascendingly explicitly', () => {
-    const store = writable(musicians);
-    const sortedStore = sorted(store, 'birthYear', 'ascending');
-    const [oldest] = get(sortedStore);
-
-    expect(oldest.name).toEqual('J Mascis');
-  });
-
-  it('should sort the items descendingly explicitly', () => {
-    const store = writable(musicians);
-    const sortedStore = sorted(store, 'birthYear', 'descending');
-    const [youngest] = get(sortedStore);
-
-    expect(youngest.name).toEqual('Bartees Strange');
-  });
-
-  it('should sort have the ability to change the sort order', () => {
-    const store = writable(musicians);
-    const sortedStore = sorted(store, 'birthYear', 'descending');
-
-    sortedStore.order.set('ascending');
-
-    const [oldest] = get(sortedStore);
-
-    expect(oldest.name).toEqual('J Mascis');
-  });
-
-  it('should sort have the ability to change property', () => {
-    const store = writable(musicians);
-    const sortedStore = sorted(store, 'birthYear', 'descending');
-
-    sortedStore.property.set('name');
-
-    const [kurt] = get(sortedStore);
-
-    expect(kurt.name).toEqual('Kurt Vile');
-  });
-});
diff --git a/src/lib/utilities/path-matches.test.ts b/src/lib/utilities/path-matches.test.ts
index bc625473..7f782444 100644
--- a/src/lib/utilities/path-matches.test.ts
+++ b/src/lib/utilities/path-matches.test.ts
@@ -40,4 +40,8 @@ describe('pathMatches', () => {
       false,
     );
   });
+
+  it('should return false if exactMatch is true and paths are not exact matches', () => {
+    expect(pathMatches('/workflows', '/workflows/events', true)).toBe(false);
+  });
 });