Skip to content

Commit c333f5d

Browse files
Merge pull request #5 from BrowserStackCE/merge-session-comparison
Merge session comparison
2 parents a03e7cd + b91a9b2 commit c333f5d

File tree

20 files changed

+1956
-55
lines changed

20 files changed

+1956
-55
lines changed

package-lock.json

Lines changed: 555 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@
2727
"@electron-forge/plugin-fuses": "^7.10.2",
2828
"@electron-forge/plugin-webpack": "^7.10.2",
2929
"@electron/fuses": "^1.8.0",
30+
"@types/diff": "^7.0.2",
3031
"@types/pouchdb": "^6.4.2",
3132
"@types/react": "^19.2.2",
3233
"@types/react-dom": "^19.2.2",
34+
"@types/react-virtualized-auto-sizer": "^1.0.4",
35+
"@types/react-window": "^1.8.8",
3336
"@typescript-eslint/eslint-plugin": "^5.62.0",
3437
"@typescript-eslint/parser": "^5.62.0",
3538
"@vercel/webpack-asset-relocator-loader": "^1.7.3",
@@ -49,17 +52,24 @@
4952
"typescript": "~4.5.4"
5053
},
5154
"dependencies": {
55+
"@monaco-editor/react": "^4.7.0",
5256
"@tailwindcss/postcss": "^4.1.15",
5357
"copy-webpack-plugin": "^13.0.1",
5458
"electron-squirrel-startup": "^1.0.1",
5559
"lodash": "^4.17.21",
60+
"monaco-editor": "^0.55.1",
61+
"monaco-types": "^0.1.0",
5662
"pouchdb": "^9.0.0",
5763
"rc-field-form": "^2.7.0",
5864
"react": "^19.2.0",
65+
"react-diff-view": "^3.3.2",
66+
"react-diff-viewer": "^3.1.1",
5967
"react-dom": "^19.2.0",
6068
"react-router-dom": "^7.9.4",
6169
"react-simple-code-editor": "^0.14.1",
6270
"react-toastify": "^11.0.5",
71+
"react-virtualized-auto-sizer": "^1.0.26",
72+
"react-window": "^2.2.3",
6373
"sugar-high": "^0.9.4"
6474
}
6575
}

src/channelHandlers/browserstack-api.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,37 @@ export const getAutomateParsedSeleniumLogs = async (session: AutomateSessionResp
228228

229229
return result;
230230
}
231+
232+
export const getSeleniumLogs = async (selenium_logs_url: string) => {
233+
if (!selenium_logs_url) {
234+
return 'No Selenium logs available for this session';
235+
}
236+
try {
237+
const response = await fetch(selenium_logs_url);
238+
if (!response.ok) {
239+
throw new Error(`HTTP error! status: ${response.status}`);
240+
}
241+
return await response.text();
242+
// For other URLs, use the existing download function with auth
243+
return await download(selenium_logs_url);
244+
} catch (error) {
245+
console.error('Failed to fetch Selenium logs:', error);
246+
return 'Failed to load Selenium logs';
247+
}
248+
}
249+
250+
export const getHarLogs = async (harLogsUrl: string) => {
251+
if (!harLogsUrl) {
252+
return 'No network logs available for this session';
253+
}
254+
try {
255+
const response = await fetch(harLogsUrl);
256+
if (!response.ok) {
257+
throw new Error(`HTTP error! status: ${response.status}`);
258+
}
259+
return await response.text();
260+
} catch (error) {
261+
console.error('Failed to fetch HAR logs:', error);
262+
return 'Failed to load network logs';
263+
}
264+
}

src/constants/ipc-channels.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ const CHANNELS = {
1111
BROWSERSTACK_EXECUTE_SESSION_COMMAND:'BROWSERSTACK_EXECUTE_SESSION_COMMAND',
1212
ELECTRON_OPEN_URL:'ELECTRON_OPEN_URL',
1313
GET_BROWSERSTACK_AUTOMATE_PARSED_SESSION_LOGS:'GET_BROWSERSTACK_AUTOMATE_PARSED_SESSION_LOGS',
14-
GET_BROWSERSTACK_AUTOMATE_PARSED_SELENIUM_LOGS:'GET_BROWSERSTACK_AUTOMATE_PARSED_SELENIUM_LOGS'
14+
GET_BROWSERSTACK_AUTOMATE_PARSED_SELENIUM_LOGS:'GET_BROWSERSTACK_AUTOMATE_PARSED_SELENIUM_LOGS',
15+
GET_BROWSERSTACK_AUTOMATE_SELENIUM_LOGS: 'GET /automate/sessions/seleniumLogs',
16+
GET_BROWSERSTACK_AUTOMATE_HAR_LOGS: 'GET /automate/sessions/harLogs'
1517
}
1618

1719
export default CHANNELS

src/global.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@ declare global {
1414
stopSession: (options: StopSessionOptions) => Promise<StopSessionResponse>
1515
executeCommand: (options: ExecuteCommandOptions) => any
1616
getAutomateParsedSessionLogs: (session: AutomateSessionResponse) => Promise<ScanResult>
17-
getAutomateParsedSeleniumLogs: (session: AutomateSessionResponse) => Promise<SeleniumScanResult>
17+
getAutomateParsedSeleniumLogs: (session: AutomateSessionResponse) => Promise<SeleniumScanResult>,
18+
getAutomateParsedTextLogs: (session:AutomateSessionResponse) => Promise<ParsedTextLogsResult>
19+
getSeleniumLogs: (selenium_logs_url: string) => Promise<string>
20+
getHarLogs: (harLogsUrl: string) => Promise<string>
1821
}
1922

2023
type ElectronAPI = {
2124
openExternalUrl: (url: string) => Promise<void>
25+
2226
}
2327

2428
interface DBItem {
@@ -41,6 +45,7 @@ declare global {
4145
title: string
4246
description: string,
4347
path: string
48+
component: React.ReactNode | null
4449
}[]
4550
}
4651

src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import StorageKeys from './constants/storage-keys';
88
import CONFIG from './constants/config';
99

1010
import { mkdirSync } from 'fs'
11-
import { executeCommand, getAutomateSessionDetails, getParsedAutomateTextLogs, startBrowserStackSession, stopBrowserStackSession, getAutomateParsedSeleniumLogs, getAutomateParsedSessionLogs, } from './channelHandlers/browserstack-api';
11+
import { executeCommand, getAutomateSessionDetails, getParsedAutomateTextLogs, startBrowserStackSession, stopBrowserStackSession, getAutomateParsedSeleniumLogs, getAutomateParsedSessionLogs,getSeleniumLogs, getHarLogs } from './channelHandlers/browserstack-api';
1212
import { openExternalUrl } from './channelHandlers/electron-api';
1313

14-
import { get } from 'http';
14+
1515
// This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack
1616
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
1717
// whether you're running in development or production).
@@ -101,6 +101,8 @@ app.whenReady().then(() => {
101101
ipcMain.handle(CHANNELS.BROWSERSTACK_STOP_SESSION, (_, options) => stopBrowserStackSession(options))
102102
ipcMain.handle(CHANNELS.BROWSERSTACK_EXECUTE_SESSION_COMMAND, (_, options) => executeCommand(options))
103103
ipcMain.handle(CHANNELS.ELECTRON_OPEN_URL, (_, url) => openExternalUrl(url))
104+
ipcMain.handle(CHANNELS.GET_BROWSERSTACK_AUTOMATE_SELENIUM_LOGS,(_, selenium_logs_url) => getSeleniumLogs(selenium_logs_url));
105+
ipcMain.handle(CHANNELS.GET_BROWSERSTACK_AUTOMATE_HAR_LOGS, (_, har_logs_url) => getHarLogs(har_logs_url));
104106
});
105107
// In this file you can include the rest of your app's specific main process
106108
// code. You can also put them in separate files and import them here.

src/preload.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const browserstackAPI: BrowserStackAPI = {
1717
executeCommand: (options) => ipcRenderer.invoke(CHANNELS.BROWSERSTACK_EXECUTE_SESSION_COMMAND, options),
1818
getAutomateParsedSessionLogs: (session)=>ipcRenderer.invoke(CHANNELS.GET_BROWSERSTACK_AUTOMATE_PARSED_SESSION_LOGS,session),
1919
getAutomateParsedSeleniumLogs: (session)=>ipcRenderer.invoke(CHANNELS.GET_BROWSERSTACK_AUTOMATE_PARSED_SELENIUM_LOGS,session),
20+
getSeleniumLogs: (selenium_logs_url) => ipcRenderer.invoke(CHANNELS.GET_BROWSERSTACK_AUTOMATE_SELENIUM_LOGS, selenium_logs_url),
21+
getHarLogs: (har_logs_url) => ipcRenderer.invoke(CHANNELS.GET_BROWSERSTACK_AUTOMATE_HAR_LOGS, har_logs_url)
2022
}
2123

2224
const electronAPI: ElectronAPI = {

src/renderer/components/sidebar.tsx

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,6 @@ const TopMenu = [
77
path: "/",
88
},
99
];
10-
const ProductsMenu = [
11-
{
12-
title: "Automate",
13-
path: "/automate",
14-
},
15-
{
16-
title: "App Automate",
17-
path: "/app-automate",
18-
},
19-
{
20-
title: "Percy",
21-
path: "/percy",
22-
},
23-
{
24-
title: "Accessibility",
25-
path: "/accessibility",
26-
},
27-
];
2810

2911
export default function Sidebar() {
3012
const location = useLocation();

src/renderer/index.css

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,41 @@
1111
--sh-string: #00a99a;
1212
--sh-keyword: #f47067;
1313
--sh-comment: #a19595;
14-
}
14+
}
15+
16+
.diff-container .diff-viewer-code-column {
17+
max-width: 600px;
18+
overflow-wrap: anywhere;
19+
}
20+
21+
.diff-container .rdv-code-wrapper {
22+
max-width: 25vw;
23+
white-space: pre-wrap !important;
24+
word-break: break-word !important;
25+
}
26+
27+
.diff-container .rdv-code {
28+
white-space: pre-wrap !important;
29+
word-break: break-word !important;
30+
}
31+
32+
/* Force wrapping in ReactDiffViewer */
33+
.react-diff-viewer,
34+
.react-diff-viewer * {
35+
white-space: pre-wrap !important;
36+
word-break: break-word !important;
37+
}
38+
39+
/* Limit each side to half of the container */
40+
.react-diff-viewer .diff-viewer-container,
41+
.react-diff-viewer .diff-viewer-split {
42+
max-width: 48vw !important;
43+
}
44+
45+
/* Override left + right code column width */
46+
.react-diff-viewer .diff-code,
47+
.react-diff-viewer .diff-line {
48+
max-width: 48vw !important;
49+
width: 48vw !important;
50+
display: block !important;
51+
}

src/renderer/products.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import AutomatePage from "./routes/automate";
22
import ReplayTool from "./routes/automate/tools/replay-tool";
33
import LatencyFinder from "./routes/automate/tools/latency-finder";
4+
import SessionComparison from "./routes/automate/tools/session-comparison";
45

56
const Products = [
67
{
@@ -21,6 +22,83 @@ const Products = [
2122
path: "/automate/latency-analyser",
2223
component: LatencyFinder,
2324
},
25+
{
26+
title: "Session Comparison",
27+
description: "Compares logs across sessions and highlights differences",
28+
path: '/automate/session-comparison',
29+
component: SessionComparison
30+
}
31+
],
32+
},
33+
{
34+
name: "App Automate",
35+
path: "/app-automate",
36+
page: AutomatePage,
37+
tools: [
38+
{
39+
title: "Replay Toolkit",
40+
description: "Replays the sessions on BrowserStack by parsing Raw Logs",
41+
path: "/automate/replay-toolkit",
42+
component: null,
43+
},
44+
{
45+
title: "Latency Analyser",
46+
description:
47+
"Analyses time spend on different actions. Helpful to identify inside/outside time for a customer session.",
48+
path: "/automate/latency-analyser",
49+
component: null,
50+
},
51+
{
52+
title: "Session Comparison",
53+
description: "Compares logs across sessions and highlights differences",
54+
path: '/automate/session-comparison',
55+
component: null
56+
}
57+
],
58+
},
59+
{
60+
name: "Percy",
61+
path: "/percy",
62+
page: AutomatePage,
63+
tools: [
64+
{
65+
title: "Snapshot Replay",
66+
description: "Replay snapshots",
67+
path: "/percy/snapshot-replay",
68+
component: null,
69+
},
70+
{
71+
title: "CLI Logs Downloader",
72+
description: "Download CLI logs using hash ID displayed in Customer's console",
73+
path: "/percy/cli-log-downloader",
74+
component: null,
75+
},
76+
],
77+
},
78+
{
79+
name: "Test Report & Analytics",
80+
path: "/tra",
81+
page: AutomatePage,
82+
tools: [
83+
{
84+
title: "SDK Logs Downloader",
85+
description: "Download SDK logs from Backend",
86+
path: "/tra/download-logs",
87+
component: null,
88+
},
89+
],
90+
},
91+
{
92+
name: "Web Accessibility",
93+
path: "/web-accessibility",
94+
page: AutomatePage,
95+
tools: [
96+
{
97+
title: "Automate Session Finder",
98+
description: "Find associated automate session for accessibility scanner run",
99+
path: "/web-a11y/session-finder",
100+
component: null,
101+
},
24102
],
25103
},
26104
];

0 commit comments

Comments
 (0)