From 1fe050d549ee891ce543bc5e2592cd54a95bef9b Mon Sep 17 00:00:00 2001
From: shmck <shawn.j.mckay@gmail.com>
Date: Fri, 24 Jul 2020 21:32:54 -0700
Subject: [PATCH 1/3] add test run loading indicator

Signed-off-by: shmck <shawn.j.mckay@gmail.com>
---
 web-app/src/containers/Tutorial/index.tsx | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/web-app/src/containers/Tutorial/index.tsx b/web-app/src/containers/Tutorial/index.tsx
index 2c5ddace..54df9770 100644
--- a/web-app/src/containers/Tutorial/index.tsx
+++ b/web-app/src/containers/Tutorial/index.tsx
@@ -179,8 +179,13 @@ const TutorialPage = (props: PageProps) => {
           {/* Left */}
           <div css={{ flex: 1 }}>
             {DISPLAY_RUN_TEST_BUTTON && level.status !== 'COMPLETE' ? (
-              <Button style={{ marginLeft: '1rem' }} type="primary" onClick={onRunTest} disabled={disableOptions}>
-                Run
+              <Button
+                style={{ marginLeft: '1rem', width: '3rem' }}
+                type="primary"
+                onClick={onRunTest}
+                disabled={disableOptions}
+              >
+                {props.state === 'Level.TestRunning' ? <Icon type="loading" size="small" /> : 'Run'}
               </Button>
             ) : null}
           </div>

From 7b995fc167719e924e1fc1070c09ec10c42a1f56 Mon Sep 17 00:00:00 2001
From: shmck <shawn.j.mckay@gmail.com>
Date: Fri, 24 Jul 2020 21:42:42 -0700
Subject: [PATCH 2/3] change Process ui

Signed-off-by: shmck <shawn.j.mckay@gmail.com>
---
 web-app/src/components/Message/index.tsx      | 45 -------------------
 .../src/components/ProcessMessages/index.tsx  |  6 ++-
 web-app/stories/Tutorial.stories.tsx          |  9 ++++
 3 files changed, 13 insertions(+), 47 deletions(-)
 delete mode 100644 web-app/src/components/Message/index.tsx

diff --git a/web-app/src/components/Message/index.tsx b/web-app/src/components/Message/index.tsx
deleted file mode 100644
index 7d2f9a11..00000000
--- a/web-app/src/components/Message/index.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Message as AlifdMessage } from '@alifd/next'
-import * as React from 'react'
-
-interface Props {
-  type?: 'success' | 'warning' | 'error' | 'notice' | 'help' | 'loading' | 'hidden'
-  shape?: 'inline' | 'addon' | 'toast'
-  size?: 'medium' | 'large'
-  title: string
-  content?: string
-  closed?: boolean
-  closeable?: boolean
-  onClose?: () => void
-  handleClose?: () => void
-  children?: React.ReactElement | null
-}
-
-const Message = (props: Props) => {
-  const [visible, setVisible] = React.useState(true)
-  if (props.type === 'hidden') {
-    return null
-  }
-  function onClose() {
-    if (props.onClose) {
-      props.onClose()
-    }
-    setVisible(false)
-  }
-  return (
-    <AlifdMessage
-      type={props.type}
-      visible={props.closed ? !props.closed : visible}
-      title={props.title}
-      closeable={props.closeable}
-      onClose={onClose}
-      shape={props.shape}
-    >
-      <div>
-        <div>{props.content}</div>
-        <div>{props.children}</div>
-      </div>
-    </AlifdMessage>
-  )
-}
-
-export default Message
diff --git a/web-app/src/components/ProcessMessages/index.tsx b/web-app/src/components/ProcessMessages/index.tsx
index fe90888d..c1460319 100644
--- a/web-app/src/components/ProcessMessages/index.tsx
+++ b/web-app/src/components/ProcessMessages/index.tsx
@@ -1,4 +1,4 @@
-import Message from '../Message'
+import { Message } from '@alifd/next'
 import * as React from 'react'
 import * as T from 'typings'
 import { css, jsx } from '@emotion/core'
@@ -22,7 +22,9 @@ const ProcessMessages = ({ processes }: Props) => {
   return (
     <div css={styles.container}>
       {processes.map((process) => (
-        <Message key={process.title} type="loading" size="medium" title={process.title} content={process.description} />
+        <Message key={process.title} type="notice" iconType="loading" size="medium" title={process.title}>
+          {process.description}
+        </Message>
       ))}
     </div>
   )
diff --git a/web-app/stories/Tutorial.stories.tsx b/web-app/stories/Tutorial.stories.tsx
index cc226800..6a70e54b 100644
--- a/web-app/stories/Tutorial.stories.tsx
+++ b/web-app/stories/Tutorial.stories.tsx
@@ -180,3 +180,12 @@ storiesOf('Tutorial', module)
     }
     return <Tutorial state="Completed" context={lastLevel} send={action('send')} />
   })
+  .add('1.1 Loading', () => {
+    const firstLevel = {
+      ...context,
+      processes: [{ title: 'Process', description: 'A process here', status: 'RUNNING' }],
+      position: { levelId: '1', stepId: '1.2' },
+      progress: { levels: {}, steps: {}, complete: false },
+    }
+    return <Tutorial state="Level.Normal" context={firstLevel} send={action('send')} />
+  })

From b682318ee2efde824cd2572561c9a5fe3093fbd2 Mon Sep 17 00:00:00 2001
From: shmck <shawn.j.mckay@gmail.com>
Date: Fri, 24 Jul 2020 21:55:43 -0700
Subject: [PATCH 3/3] show error message if issue with subtasks

Signed-off-by: shmck <shawn.j.mckay@gmail.com>
---
 .../src/containers/Tutorial/formatLevels.ts   | 41 ++++++++++++-------
 1 file changed, 27 insertions(+), 14 deletions(-)

diff --git a/web-app/src/containers/Tutorial/formatLevels.ts b/web-app/src/containers/Tutorial/formatLevels.ts
index 54fb443a..9002eb96 100644
--- a/web-app/src/containers/Tutorial/formatLevels.ts
+++ b/web-app/src/containers/Tutorial/formatLevels.ts
@@ -45,20 +45,33 @@ const formatLevels = ({ progress, position, levels, testStatus }: Input): Output
         status = 'ACTIVE'
       }
       if (step.subtasks && step.subtasks) {
-        subtasks = step.subtasks.map((subtask: string, subtaskIndex: number) => {
-          let subtaskStatus: T.ProgressStatus = 'INCOMPLETE'
-          // task is complete, subtasks must be complete
-          if (status === 'COMPLETE') {
-            subtaskStatus = 'COMPLETE'
-            // task is active, check which are complete from test results
-          } else if (status === 'ACTIVE') {
-            subtaskStatus = !!(testStatus?.summary && testStatus.summary[subtaskIndex]) ? 'COMPLETE' : 'ACTIVE'
-          }
-          return {
-            name: subtask,
-            status: subtaskStatus,
-          }
-        })
+        if (Object.keys(testStatus?.summary || {}).length !== step.subtasks.length) {
+          // test result count and subtask count don't match
+          // something is wrong with the tutorial
+          // NOTE: hacky temp solution as should be caught by tutorial creators / build tools
+          subtasks = [
+            {
+              name:
+                'ERROR: subtasks and test results have a different number of results. This is likely an error with the tutorial.',
+              status: 'ACTIVE' as 'ACTIVE',
+            },
+          ]
+        } else {
+          subtasks = step.subtasks.map((subtask: string, subtaskIndex: number) => {
+            let subtaskStatus: T.ProgressStatus = 'INCOMPLETE'
+            // task is complete, subtasks must be complete
+            if (status === 'COMPLETE') {
+              subtaskStatus = 'COMPLETE'
+              // task is active, check which are complete from test results
+            } else if (status === 'ACTIVE') {
+              subtaskStatus = !!(testStatus?.summary && testStatus.summary[subtaskIndex]) ? 'COMPLETE' : 'ACTIVE'
+            }
+            return {
+              name: subtask,
+              status: subtaskStatus,
+            }
+          })
+        }
       }
       return { ...step, status, subtasks }
     }),