-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: drop down of apps from app management #113
Changes from 19 commits
a944a50
fb00c75
12f4927
5be9760
1f31469
f70bbdb
69b9fdf
6fb6d9c
501ce47
7117b70
64c078e
792b8f7
f23b594
bc38521
f472a10
ff56521
e99dd58
f2508ad
7b7798b
47444a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -19,6 +19,7 @@ | |||
import { AssertionInput } from './AssertionInput'; | ||||
import { Preview } from './Preview'; | ||||
import { AbstractBaseGenerator, AppiumPython, AppiumJava } from './codegen'; | ||||
import { AppInfo } from '../../../src/types'; | ||||
|
||||
function App() { | ||||
const [state, dispatch] = useReducer(reducer, initialState); | ||||
|
@@ -26,7 +27,7 @@ | |||
const [showAdditionalSettings, setShowAdditionalSettings] = useState(false); | ||||
|
||||
useEffect(() => { | ||||
function handler(event: any) { | ||||
const message = event.data as PostedMessage; // The json data that the extension sent | ||||
|
||||
switch (message.action) { | ||||
|
@@ -66,6 +67,12 @@ | |||
value: message.data, | ||||
}); | ||||
break; | ||||
case 'load-app-names': | ||||
dispatch({ | ||||
type: 'loadAppNames', | ||||
value: message.data, | ||||
}); | ||||
break; | ||||
case 'recover-from-error': | ||||
dispatch({ | ||||
type: 'setGenerationState', | ||||
|
@@ -115,26 +122,45 @@ | |||
status, | ||||
steps, | ||||
tunnel, | ||||
apps, | ||||
} = state; | ||||
|
||||
return ( | ||||
<main> | ||||
<section className="inputs"> | ||||
<h2>What do you want to test?</h2> | ||||
<VSCodeTextField | ||||
value={appName} | ||||
placeholder="From Sauce Labs App Storage, e.g. test.apk" | ||||
onInput={(e) => { | ||||
if (e.target && 'value' in e.target) { | ||||
dispatch({ | ||||
type: 'setAppName', | ||||
value: e.target.value as string, | ||||
}); | ||||
} | ||||
}} | ||||
> | ||||
Application Name | ||||
</VSCodeTextField> | ||||
<section className="with-label"> | ||||
<label>App Name</label> | ||||
<VSCodeDropdown | ||||
onInput={(e) => { | ||||
if (e.target && 'value' in e.target) { | ||||
const appName: string = e.target.value as string; | ||||
const appInfo = apps.find( | ||||
(app) => app.name === appName, | ||||
) as AppInfo; | ||||
dispatch({ | ||||
type: 'setAppName', | ||||
value: { | ||||
appName: appName as string, | ||||
platformName: appInfo.kind as 'iOS' | 'Android', | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are still forcing types onto the compiler though 😅 If there's a bug in
Options
|
||||
}, | ||||
}); | ||||
} | ||||
}} | ||||
value={appName} | ||||
className="app-list" | ||||
> | ||||
{appName !== '' && | ||||
!apps.some((appInfo) => appInfo.name === appName) && | ||||
apps.length > 0 && | ||||
!apps.some((appInfo) => appInfo.name === appName) ? ( | ||||
mhan83 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
<VSCodeOption className="app-not-loaded">{appName}</VSCodeOption> | ||||
) : null} | ||||
{apps.map((appInfo) => ( | ||||
<VSCodeOption>{appInfo.name}</VSCodeOption> | ||||
))} | ||||
</VSCodeDropdown> | ||||
</section> | ||||
<VSCodeTextArea | ||||
resize="both" | ||||
value={testGoal} | ||||
|
@@ -186,7 +212,7 @@ | |||
}} | ||||
value={state.tunnel?.name ?? ''} | ||||
> | ||||
Tunnel Name | ||||
Tunnel Name (optional) | ||||
</VSCodeTextField> | ||||
<VSCodeTextField | ||||
placeholder="Sauce Connect tunnel owner" | ||||
|
@@ -200,7 +226,7 @@ | |||
}} | ||||
value={state.tunnel?.owner ?? ''} | ||||
> | ||||
Tunnel Owner | ||||
Tunnel Owner (optional) | ||||
</VSCodeTextField> | ||||
<VSCodeTextField | ||||
value={maxSteps.toString()} | ||||
|
@@ -223,23 +249,6 @@ | |||
> | ||||
Cut off steps at | ||||
</VSCodeTextField> | ||||
<section className="with-label"> | ||||
<label>Platform</label> | ||||
<VSCodeDropdown | ||||
onInput={(e) => { | ||||
if (e.target && 'value' in e.target) { | ||||
dispatch({ | ||||
type: 'setPlatformName', | ||||
value: e.target.value as 'iOS' | 'Android', | ||||
}); | ||||
} | ||||
}} | ||||
value={platform.name} | ||||
> | ||||
<VSCodeOption>Android</VSCodeOption> | ||||
<VSCodeOption>iOS</VSCodeOption> | ||||
</VSCodeDropdown> | ||||
</section> | ||||
<VSCodeTextField | ||||
onInput={(e) => { | ||||
if (e.target && 'value' in e.target) { | ||||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -1,5 +1,5 @@ | ||||||||
import { v4 as uuidv4 } from 'uuid'; | ||||||||
import { Credentials, TestRecord, Vote } from '../../../src/types'; | ||||||||
import { Credentials, TestRecord, Vote, AppInfo } from '../../../src/types'; | ||||||||
|
||||||||
export interface Assertion { | ||||||||
value: string; | ||||||||
|
@@ -78,6 +78,8 @@ export interface State { | |||||||
language: 'python' | 'java'; | ||||||||
|
||||||||
tunnel: Tunnel; | ||||||||
|
||||||||
apps: AppInfo[]; | ||||||||
} | ||||||||
|
||||||||
export const initialState: State = { | ||||||||
|
@@ -102,14 +104,20 @@ export const initialState: State = { | |||||||
name: '', | ||||||||
owner: '', | ||||||||
}, | ||||||||
apps: [], | ||||||||
}; | ||||||||
|
||||||||
export type Action = | ||||||||
| { type: 'clear' } | ||||||||
| { type: 'setAppName'; value: State['appName'] } | ||||||||
| { | ||||||||
type: 'setAppName'; | ||||||||
value: { | ||||||||
appName: State['appName']; | ||||||||
platformName: State['platform']['name']; | ||||||||
}; | ||||||||
} | ||||||||
| { type: 'setTestGoal'; value: State['testGoal'] } | ||||||||
| { type: 'setMaxSteps'; value: string } | ||||||||
| { type: 'setPlatformName'; value: State['platform']['name'] } | ||||||||
| { type: 'setPlatformVersion'; value: State['platform']['version'] } | ||||||||
| { type: 'setStatus'; value: State['status'] } | ||||||||
| { type: 'setGenerationState'; value: State['generationState'] } | ||||||||
|
@@ -118,6 +126,7 @@ export type Action = | |||||||
| { type: 'setTunnelOwner'; value: State['tunnel']['owner'] } | ||||||||
| { type: 'showTestRecord'; value: { testRecord: TestRecord; votes: Vote[] } } | ||||||||
| { type: 'loadNewRecord'; value: TestRecord } | ||||||||
| { type: 'loadAppNames'; value: AppInfo[] } | ||||||||
| { type: 'addAssertion'; value: { key: string } } | ||||||||
| { type: 'removeAssertion'; value: { key: string } } | ||||||||
| { type: 'setAssertionValue'; value: { key: string; value: string } } | ||||||||
|
@@ -247,7 +256,11 @@ export const reducer = (current: State, action: Action): State => { | |||||||
case 'setAppName': | ||||||||
return { | ||||||||
...current, | ||||||||
appName: action.value, | ||||||||
appName: action.value.appName, | ||||||||
platform: { | ||||||||
...current.platform, | ||||||||
name: action.value.platformName, | ||||||||
}, | ||||||||
}; | ||||||||
case 'setTestGoal': | ||||||||
return { | ||||||||
|
@@ -263,14 +276,6 @@ export const reducer = (current: State, action: Action): State => { | |||||||
maxSteps, | ||||||||
}; | ||||||||
} | ||||||||
case 'setPlatformName': | ||||||||
return { | ||||||||
...current, | ||||||||
platform: { | ||||||||
...current.platform, | ||||||||
name: action.value, | ||||||||
}, | ||||||||
}; | ||||||||
case 'setPlatformVersion': | ||||||||
return { | ||||||||
...current, | ||||||||
|
@@ -360,6 +365,26 @@ export const reducer = (current: State, action: Action): State => { | |||||||
}, | ||||||||
}; | ||||||||
} | ||||||||
case 'loadAppNames': { | ||||||||
let apps = action.value; | ||||||||
apps = apps.filter( | ||||||||
(item) => | ||||||||
item.metadata === null || | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a wee bit confused, since according to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since metadata might be null, though rare, I'll allow metadata to be null in the type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd vote for adding it to the interface. The downside is a few extra null checks, upside is an accurate and reliable type. |
||||||||
!('is_simulator' in item.metadata) || | ||||||||
item.metadata.is_simulator === false, | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be simplified:
Suggested change
|
||||||||
); | ||||||||
apps.forEach((app) => { | ||||||||
app.kind = app.kind === 'android' ? 'Android' : 'iOS'; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See my other comments w/r/t/ massaging the data at the HTTP client level or type guarding. |
||||||||
}); | ||||||||
if (apps) { | ||||||||
apps.sort((a, b) => a.name.localeCompare(b.name)); | ||||||||
} | ||||||||
|
||||||||
return { | ||||||||
...current, | ||||||||
apps: apps, | ||||||||
}; | ||||||||
} | ||||||||
case 'showTestRecord': { | ||||||||
const { testRecord, votes } = action.value; | ||||||||
let { user_screen_descs = [''] } = testRecord; | ||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vscode-scriptiq/webview-ui/test-generation/src/App.tsx
Line 137 in 7b7798b