Skip to content

Commit cd8d2fc

Browse files
authored
style: render discovered sessions as card grid (#2758)
* style: render discovered sessions as card grid * start updating docs * simplify translation key * update docs * prettier * address comments * address comments * fix tests
1 parent f04134c commit cd8d2fc

File tree

13 files changed

+166
-124
lines changed

13 files changed

+166
-124
lines changed

app/common/public/locales/en/translation.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,13 @@
152152
"number": "number",
153153
"JSON object": "JSON object",
154154
"connectToExistingSessionInstructions": "If you have an already-running session of the above server type, you can attach an inspector to it directly.",
155-
"selectSessionID": "You can manually enter the ID of the session you want to attach to, or, if the server supports session discovery, simply select from a list of active sessions.",
155+
"selectSessionID": "You can manually enter your session ID, or, if the server supports session discovery, simply select from a list of active sessions.",
156156
"allowUnauthorizedCerts": "Allow Unauthorized Certificates",
157157
"Use Proxy": "Use Proxy",
158158
"JSON Representation": "JSON Representation",
159159
"Capability Builder": "Capability Builder",
160160
"Saved Capability Sets": "Saved Capability Sets",
161-
"Attach to Session": "Attach to Session…",
161+
"Attach": "Attach",
162162
"localhost": "localhost",
163163
"Host": "Host",
164164
"Port": "Port",

app/common/renderer/actions/SessionBuilder.js

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ export const CHANGE_SERVER_TYPE = 'CHANGE_SERVER_TYPE';
5353
export const SET_SERVER_PARAM = 'SET_SERVER_PARAM';
5454
export const SET_SERVER = 'SET_SERVER';
5555

56-
export const SET_ATTACH_SESS_ID = 'SET_ATTACH_SESS_ID';
57-
5856
export const GET_SESSIONS_REQUESTED = 'GET_SESSIONS_REQUESTED';
5957
export const GET_SESSIONS_DONE = 'GET_SESSIONS_DONE';
6058

@@ -510,15 +508,6 @@ export function deleteSavedSession(uuid) {
510508
};
511509
}
512510

513-
/**
514-
* Set the session id to attach to
515-
*/
516-
export function setAttachSessId(attachSessId) {
517-
return (dispatch) => {
518-
dispatch({type: SET_ATTACH_SESS_ID, attachSessId});
519-
};
520-
}
521-
522511
/**
523512
* Change the server type
524513
*/
@@ -746,7 +735,7 @@ export function getRunningSessions() {
746735
return async (dispatch, getState) => {
747736
const avoidServerTypes = ['sauce'];
748737
const state = getState().builder;
749-
const {server, serverType, attachSessId} = state;
738+
const {server, serverType} = state;
750739
const vendorProperties = await retrieveVendorProperties({
751740
server,
752741
serverType,
@@ -793,17 +782,6 @@ export function getRunningSessions() {
793782
const baseUrl = `${protocol}://${host}:${port}${adjPath}`;
794783
const sessions = await fetchAllSessions(baseUrl, headers);
795784
dispatch({type: GET_SESSIONS_DONE, sessions});
796-
797-
// set attachSessId if only one session found
798-
if (sessions.length === 1) {
799-
dispatch({type: SET_ATTACH_SESS_ID, attachSessId: sessions[0].id});
800-
} else if (attachSessId) {
801-
// clear attachSessId if it is no longer present in the found session list
802-
const attachSessIdFound = sessions.find((session) => session.id === attachSessId);
803-
if (!attachSessIdFound) {
804-
dispatch({type: SET_ATTACH_SESS_ID, attachSessId: null});
805-
}
806-
}
807785
};
808786
}
809787

@@ -1063,6 +1041,7 @@ export function initFromQueryString(loadNewSession) {
10631041
}
10641042

10651043
if (autoStartSession === AUTO_START_URL_PARAM) {
1044+
// at this point these can only be set using SET_STATE_FROM_URL
10661045
const {attachSessId, caps} = getState().builder;
10671046
if (attachSessId) {
10681047
return loadNewSession(null, attachSessId);

app/common/renderer/components/SessionBuilder/AttachToSessionTab/AttachToSession.jsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {Empty, Space, Spin} from 'antd';
22
import {useTranslation} from 'react-i18next';
33

4-
import {getSessionInfo} from '../../../utils/attaching-to-session.js';
54
import styles from './AttachToSession.module.css';
65
import AttachToSessionInstructions from './AttachToSessionInstructions.jsx';
76
import DiscoveredSessions from './DiscoveredSessions.jsx';
@@ -12,19 +11,12 @@ import ManualIdInputAndRefreshBtn from './ManualIdInputAndRefreshBtn.jsx';
1211
*/
1312
const AttachToSession = ({
1413
serverType,
15-
attachSessId,
16-
setAttachSessId,
1714
runningAppiumSessions,
1815
gettingSessions,
1916
getRunningSessions,
2017
loadNewSession,
2118
}) => {
2219
const {t} = useTranslation();
23-
// list is reversed in order to place the most recent sessions at the top
24-
// slice() is added because reverse() mutates the original array
25-
const sortedRunningSessions = [...runningAppiumSessions]
26-
.reverse()
27-
.map((session) => ({value: session.id, label: getSessionInfo(session, serverType)}));
2820

2921
return (
3022
<Space className={styles.spaceContainer} orientation="vertical" size="large">
@@ -34,11 +26,11 @@ const AttachToSession = ({
3426
getRunningSessions={getRunningSessions}
3527
/>
3628
<Spin spinning={gettingSessions}>
37-
{sortedRunningSessions.length !== 0 ? (
29+
{runningAppiumSessions.length !== 0 ? (
3830
<DiscoveredSessions
39-
attachSessId={attachSessId}
40-
setAttachSessId={setAttachSessId}
41-
sortedRunningSessions={sortedRunningSessions}
31+
runningAppiumSessions={runningAppiumSessions}
32+
serverType={serverType}
33+
loadNewSession={loadNewSession}
4234
/>
4335
) : (
4436
<Empty description={t('noRunningSessionsFound')} image={Empty.PRESENTED_IMAGE_SIMPLE} />
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import {
2+
IconApps,
3+
IconClockHour4,
4+
IconDeviceMobile,
5+
IconDeviceMobileCog,
6+
IconLinkPlus,
7+
IconTag,
8+
} from '@tabler/icons-react';
9+
import {Button, Card, Col, Flex, Row, Space, Typography} from 'antd';
10+
import {useTranslation} from 'react-i18next';
11+
12+
import {BUTTON} from '../../../constants/antd-types.js';
13+
import {getSessionInfo} from '../../../utils/attaching-to-session.js';
14+
import styles from './AttachToSession.module.css';
15+
16+
const addIcon = (Icon, label) => (
17+
<Flex gap={4} align="center">
18+
<Icon size={18} />
19+
<Typography.Text ellipsis={true}>{label}</Typography.Text>
20+
</Flex>
21+
);
22+
23+
/**
24+
* Card for a single discovered session.
25+
*/
26+
const DiscoveredSessionCard = ({session, serverType, loadNewSession}) => {
27+
const {t} = useTranslation();
28+
const sessionDetails = getSessionInfo(session, serverType);
29+
30+
return (
31+
<Card hoverable={true} styles={{root: {height: '100%'}, body: {padding: '8px'}}}>
32+
<Space className={styles.spaceContainer} orientation="vertical" size="small">
33+
<Row justify="space-between" align="middle">
34+
<Typography.Text code ellipsis={true}>
35+
{sessionDetails.id}
36+
</Typography.Text>
37+
<Button
38+
type={BUTTON.PRIMARY}
39+
icon={<IconLinkPlus size={18} />}
40+
onClick={() => loadNewSession(null, sessionDetails.id)}
41+
>
42+
{t('Attach')}
43+
</Button>
44+
</Row>
45+
{sessionDetails.sessionName && <Row>{addIcon(IconTag, sessionDetails.sessionName)}</Row>}
46+
<Row>
47+
{sessionDetails.deviceId && (
48+
<Col span={12}>{addIcon(IconDeviceMobile, sessionDetails.deviceId)}</Col>
49+
)}
50+
<Col span={12}>{addIcon(IconDeviceMobileCog, sessionDetails.platformInfo)}</Col>
51+
</Row>
52+
{sessionDetails.appId || sessionDetails.timestamp ? (
53+
<Row>
54+
{sessionDetails.appId && <Col span={12}>{addIcon(IconApps, sessionDetails.appId)}</Col>}
55+
{sessionDetails.timestamp && (
56+
<Col span={12}>{addIcon(IconClockHour4, sessionDetails.timestamp)}</Col>
57+
)}
58+
</Row>
59+
) : null}
60+
</Space>
61+
</Card>
62+
);
63+
};
64+
65+
export default DiscoveredSessionCard;

app/common/renderer/components/SessionBuilder/AttachToSessionTab/DiscoveredSessions.jsx

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
import {Col, Row, Select} from 'antd';
2-
import {useTranslation} from 'react-i18next';
1+
import {Col, Row} from 'antd';
2+
3+
import DiscoveredSessionCard from './DiscoveredSessionCard.jsx';
34

45
/**
5-
* Selection dropdown for all discovered sessions.
6+
* Grid container for all discovered sessions.
67
*/
7-
const DiscoveredSessions = ({attachSessId, setAttachSessId, sortedRunningSessions}) => {
8-
const {t} = useTranslation();
8+
const DiscoveredSessions = ({runningAppiumSessions, serverType, loadNewSession}) => {
9+
// list is reversed in order to place the most recent sessions at the top
10+
const sortedRunningSessions = [...runningAppiumSessions].reverse();
911

1012
return (
11-
<Row>
12-
<Col span={24}>
13-
<Select
14-
style={{width: '100%'}}
15-
showSearch
16-
placeholder={t('searchSessions')}
17-
value={attachSessId || undefined}
18-
onChange={(value) => setAttachSessId(value)}
19-
options={sortedRunningSessions}
20-
/>
21-
</Col>
13+
<Row gutter={[12, 12]}>
14+
{sortedRunningSessions.map((session) => (
15+
<Col xs={12} sm={12} md={12} lg={12} xl={8} xxl={6} key={session.id}>
16+
<DiscoveredSessionCard
17+
session={session}
18+
serverType={serverType}
19+
loadNewSession={loadNewSession}
20+
/>
21+
</Col>
22+
))}
2223
</Row>
2324
);
2425
};

app/common/renderer/components/SessionBuilder/SessionBuilder.jsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ const Session = (props) => {
4848
newSession,
4949
savedSessions,
5050
newSessionLoading,
51-
attachSessId,
5251
setLocalServerParams,
5352
getSavedSessions,
5453
setSavedServerParams,
@@ -173,7 +172,7 @@ const Session = (props) => {
173172
children: <SavedCapabilitySets {...props} />,
174173
},
175174
{
176-
label: t('Attach to Session'),
175+
label: t('attachToSession'),
177176
key: SESSION_BUILDER_TABS.ATTACH_TO_SESSION,
178177
className: styles.scrollingTab,
179178
children: <AttachToSession loadNewSession={loadNewSession} {...props} />,
@@ -218,15 +217,6 @@ const Session = (props) => {
218217
{t('startSession')}
219218
</Button>
220219
)}
221-
{isAttaching && (
222-
<Button
223-
type={BUTTON.PRIMARY}
224-
disabled={!attachSessId}
225-
onClick={() => loadNewSession(null, attachSessId)}
226-
>
227-
{t('attachToSession')}
228-
</Button>
229-
)}
230220
</div>
231221
</div>
232222
</Spin>,

app/common/renderer/reducers/SessionBuilder.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import {
2727
SESSION_UPLOAD_DONE,
2828
SESSION_UPLOAD_REQUESTED,
2929
SET_ADD_VENDOR_PREFIXES,
30-
SET_ATTACH_SESS_ID,
3130
SET_CAPABILITY_NAME_ERROR,
3231
SET_CAPABILITY_PARAM,
3332
SET_CAPS_AND_SERVER,
@@ -259,12 +258,6 @@ export default function builder(state = INITIAL_STATE, action) {
259258
serverType: action.serverType || SERVER_TYPES.LOCAL,
260259
};
261260

262-
case SET_ATTACH_SESS_ID:
263-
return {
264-
...state,
265-
attachSessId: action.attachSessId,
266-
};
267-
268261
case GET_SESSIONS_REQUESTED:
269262
return {
270263
...state,

app/common/renderer/utils/attaching-to-session.js

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import _ from 'lodash';
33

44
import {SERVER_TYPES} from '../constants/session-builder.js';
55

6-
class DefaultSessionDescription {
6+
class DefaultKeySessionCaps {
77
constructor(caps) {
88
this._caps = caps;
99
}
@@ -13,7 +13,7 @@ class DefaultSessionDescription {
1313
return this._caps.sessionName;
1414
}
1515

16-
_fetchDeviceInfo() {
16+
_fetchDeviceIdentifier() {
1717
return this._caps.deviceName || this._caps.avd || this._caps.udid;
1818
}
1919

@@ -22,57 +22,61 @@ class DefaultSessionDescription {
2222
const platformInfo = this._caps.platformVersion
2323
? `${this._caps.platformName} ${this._caps.platformVersion}`
2424
: this._caps.platformName;
25-
return platformInfo;
25+
// Capabilities may not have automationName for e.g. Selenium Grid
26+
return this._caps.automationName
27+
? `${platformInfo} (${this._caps.automationName})`
28+
: platformInfo;
2629
}
27-
}
28-
29-
_fetchAutomationName() {
3030
return this._caps.automationName;
3131
}
3232

33-
_fetchAppInfo() {
33+
_fetchAppIdentifier() {
3434
return this._caps.app || this._caps.bundleId || this._caps.appPackage;
3535
}
3636

3737
assemble() {
38-
const suffixItems = [
39-
this._fetchSessionName(),
40-
this._fetchDeviceInfo(),
41-
this._fetchPlatformInfo(),
42-
this._fetchAutomationName(),
43-
this._fetchAppInfo(),
44-
];
45-
return _.compact(suffixItems).join(' / ');
38+
return {
39+
sessionName: this._fetchSessionName(),
40+
deviceId: this._fetchDeviceIdentifier(),
41+
platformInfo: this._fetchPlatformInfo(),
42+
appId: this._fetchAppIdentifier(),
43+
};
4644
}
4745
}
4846

49-
class TestMuAISessionDescription extends DefaultSessionDescription {
47+
class TestMuAIKeySessionCaps extends DefaultKeySessionCaps {
5048
constructor(caps) {
5149
super('capabilities' in caps ? caps.capabilities : caps);
5250
}
5351

54-
_fetchDeviceInfo() {
52+
_fetchDeviceIdentifier() {
5553
return 'desired' in this._caps ? this._caps.desired.deviceName : this._caps.deviceName;
5654
}
5755
}
5856

59-
const getSessionDescription = (caps, serverType) => {
57+
const getKeySessionCaps = (caps, serverType) => {
6058
switch (serverType) {
6159
case SERVER_TYPES.TESTMUAI:
62-
return new TestMuAISessionDescription(caps);
60+
return new TestMuAIKeySessionCaps(caps);
6361
default:
64-
return new DefaultSessionDescription(caps);
62+
return new DefaultKeySessionCaps(caps);
6563
}
6664
};
6765

6866
export const getSessionInfo = (session, serverType) => {
69-
let identifier = session.id;
67+
let timestamp;
7068
if ('created' in session && !_.isUndefined(session.created)) {
71-
// For Appium 3+ sessions, replace session ID with timestamp
72-
identifier = new Date(session.created).toJSON();
69+
// Add the timestamp for Appium 3+ sessions
70+
timestamp = new Date(session.created).toJSON();
7371
}
74-
const description = getSessionDescription(session.capabilities, serverType).assemble();
75-
return `${identifier}${description}`;
72+
73+
const keyCaps = getKeySessionCaps(session.capabilities, serverType).assemble();
74+
75+
return {
76+
id: session.id,
77+
timestamp,
78+
...keyCaps,
79+
};
7680
};
7781

7882
// Make a session-related HTTP GET request to the provided Appium server URL
-6.62 KB
Loading
5.53 KB
Loading

0 commit comments

Comments
 (0)