Skip to content

Commit c1a3ed5

Browse files
Copilotkobenguyent
andauthored
fix: multiple UI critical issues: autocomplete, file loading, search clearing, and stop functionality (#567)
* Initial plan * Fix multiple UI issues: autocomplete, file loading, search clearing, and stop functionality Co-authored-by: kobenguyent <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: kobenguyent <[email protected]>
1 parent c9ab7ae commit c1a3ed5

File tree

6 files changed

+90
-27
lines changed

6 files changed

+90
-27
lines changed

lib/api/stop.js

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,40 @@ const { event } = require('codeceptjs');
55
module.exports = async (req, res) => {
66
debug('Stopping test execution');
77

8-
if (global.runner) {
9-
global.runner.abort();
10-
11-
// Ensure we properly signal test completion and reset running state
12-
event.dispatcher.once(event.all.result, () => {
13-
global.runner._abort = false;
14-
debug('Test runner stopped and reset');
15-
// Emit exit event to reset frontend running state
16-
wsEvents.codeceptjs.exit(-1); // -1 indicates stopped by user
17-
});
18-
} else {
19-
// If no runner is active, still emit exit event to reset frontend state
20-
debug('No active runner found, resetting state');
8+
try {
9+
if (global.runner) {
10+
debug('Aborting active runner');
11+
global.runner.abort();
12+
13+
// Set a timeout to ensure we don't wait forever
14+
const timeout = setTimeout(() => {
15+
debug('Stop timeout reached, forcing exit event');
16+
wsEvents.codeceptjs.exit(-1);
17+
global.runner = null;
18+
}, 5000); // 5 second timeout
19+
20+
// Ensure we properly signal test completion and reset running state
21+
event.dispatcher.once(event.all.result, () => {
22+
clearTimeout(timeout);
23+
if (global.runner) {
24+
global.runner._abort = false;
25+
}
26+
global.runner = null;
27+
debug('Test runner stopped and reset');
28+
// Emit exit event to reset frontend running state
29+
wsEvents.codeceptjs.exit(-1); // -1 indicates stopped by user
30+
});
31+
} else {
32+
// If no runner is active, still emit exit event to reset frontend state
33+
debug('No active runner found, resetting state');
34+
wsEvents.codeceptjs.exit(-1);
35+
}
36+
37+
return res.status(200).send('OK');
38+
} catch (error) {
39+
debug('Error stopping test execution:', error);
40+
// Always emit exit event to reset state even if there's an error
2141
wsEvents.codeceptjs.exit(-1);
42+
return res.status(500).send('Failed to stop execution');
2243
}
23-
24-
return res.status(200).send('OK');
2544
};

lib/model/editor-repository.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ class EditorRepository {
2929

3030
// Find the scenario starting at or near the line number
3131
let startIndex = Math.max(0, lineNumber - 1);
32-
let foundScenario = false;
3332

3433
// Search for the closest Scenario definition to the target line
3534
let bestMatch = null;
@@ -51,7 +50,6 @@ class EditorRepository {
5150
}
5251

5352
startIndex = bestMatch;
54-
foundScenario = true;
5553

5654
// Find the end of the scenario by matching braces
5755
let braceCount = 0;

src/components/RunButton.vue

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,31 @@ export default {
6262
this.$emit('run');
6363
},
6464
stop() {
65+
// Emit CLI stop command first
6566
this.$socket.emit('cli.line', 'exit');
6667
this.$store.commit('cli/stopCli');
67-
this.$store.dispatch('testRuns/stop');
68-
this.disabled = true;
69-
setTimeout(() => {
70-
this.canBeStopped = false;
71-
this.disabled = false;
72-
}, 3000); // wait for 3 secs to stop
68+
69+
// Call the stop API endpoint
70+
this.$store.dispatch('testRuns/stop')
71+
.then(() => {
72+
this.disabled = true;
73+
// Wait for backend to signal completion via WebSocket
74+
setTimeout(() => {
75+
this.canBeStopped = false;
76+
this.disabled = false;
77+
}, 3000); // wait for 3 secs to reset UI state
78+
})
79+
.catch((error) => {
80+
console.error('Error stopping tests:', error);
81+
// Even if API call fails, reset UI state
82+
this.disabled = true;
83+
setTimeout(() => {
84+
this.canBeStopped = false;
85+
this.disabled = false;
86+
// Force UI state reset
87+
this.$store.commit('testRuns/setRunning', false);
88+
}, 2000);
89+
});
7390
},
7491
7592
}

src/components/ScenarioSource.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export default {
274274
this.mode = 'file';
275275
}
276276
277-
const { data } = response.data;
277+
const data = response.data.data;
278278
this.editorContent = data.source || data.content;
279279
this.originalContent = this.editorContent;
280280
this.currentStartLine = data.startLine || 1;
@@ -364,6 +364,9 @@ export default {
364364
});
365365
}
366366
367+
// Setup autocomplete now that Monaco is fully loaded
368+
this.setupAutocomplete();
369+
367370
// Focus editor
368371
editor.focus();
369372
},

src/components/pages/ScenariosPage.vue

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,11 @@ export default {
249249
},
250250
clearSearch() {
251251
this.search = '';
252+
// Clear timeout to prevent race conditions
253+
if (this.loadProjectTimeout) {
254+
clearTimeout(this.loadProjectTimeout);
255+
this.loadProjectTimeout = null;
256+
}
252257
this.loadProject(true);
253258
},
254259
@@ -306,23 +311,37 @@ export default {
306311
// Debounce the search to improve performance
307312
if (this.loadProjectTimeout) {
308313
clearTimeout(this.loadProjectTimeout);
314+
this.loadProjectTimeout = null;
309315
}
310316
311317
if (!immediate) {
312318
this.loadProjectTimeout = setTimeout(() => this.loadProject(true), 300);
313319
return;
314320
}
315321
322+
// Prevent concurrent loading
323+
if (this.loading) {
324+
return;
325+
}
326+
316327
this.updateUrl();
317328
318329
this.loading = true;
319330
const loadingComponent = this.$buefy.loading.open({ container: null });
320331
321332
try {
322333
const response = await axios.get(
323-
`/api/scenarios?q=${this.search}&m=${this.matchType}`
334+
`/api/scenarios?q=${encodeURIComponent(this.search)}&m=${this.matchType}`
324335
);
325336
this.project = response.data;
337+
} catch (error) {
338+
console.error('Error loading project:', error);
339+
// Show error message to user
340+
this.$buefy.toast.open({
341+
message: 'Failed to load test scenarios',
342+
type: 'is-danger',
343+
duration: 3000
344+
});
326345
} finally {
327346
this.loading = false;
328347
loadingComponent.close();

src/store/modules/testruns.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,15 @@ const testRuns = {
122122
commit('setRunning', true);
123123
},
124124

125-
stop: async function () {
126-
await axios.post('/api/scenarios/stop', {});
125+
stop: async function ({ commit }) {
126+
try {
127+
await axios.post('/api/scenarios/stop', {});
128+
} catch (error) {
129+
console.error('Error calling stop API:', error);
130+
// Force reset running state even if API call fails
131+
commit('setRunning', false);
132+
throw error;
133+
}
127134
},
128135

129136
runGrep: async function ({ commit }, grep) {

0 commit comments

Comments
 (0)