Skip to content

Commit 517657b

Browse files
authored
Fix isTestFile, ITE race, hover-tip cleanup, and dialog Enter key (#354)
Add cd.isTestFile() to identify files whose stem starts or ends with test/tests/spec/steps. Use it to colour filenames steel-blue in the review diff panel and hover-tips, and to simplify inter-test event icons to file_test/file_code. Fix a race where clicking a filename fired saveAnyFileChangesITE asynchronously, allowing a concurrent create/delete/rename/test to use a stale kata.index. The fix locks all CodeMirror editors and gates create/delete/rename/test behind cd.waitForITE(), which polls up to 2s for the in-flight ITE to settle. Clean up hover-tips: remove the traffic-light image and type-marker glyph column; colour filenames by file type. Fix Enter key in the delete file dialog, where the disabled input blocked the keyup handler. Handling moved to dialog-level keydown with event.preventDefault() to suppress browser default submit.
1 parent 78e0732 commit 517657b

9 files changed

Lines changed: 84 additions & 30 deletions

source/app/assets/javascripts/cyber-dojo_codemirror.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ var cyberDojo = ((cd, $) => {
7777

7878
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
7979

80+
cd.setFilesEditable = (editable) => {
81+
$.each($('.CodeMirror'), (i, editorDiv) => {
82+
editorDiv.CodeMirror.setOption('readOnly', !editable);
83+
});
84+
};
85+
86+
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
87+
8088
cd.saveCodeFromIndividualSyntaxHighlightEditor = (filename) => {
8189
const editorDiv = document.getElementById(syntaxHighlightFileContentForId(filename));
8290
editorDiv.CodeMirror.cyberDojoTextArea.value = editorDiv.CodeMirror.getValue();

source/app/assets/javascripts/cyber-dojo_hover_tips.js

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ var cyberDojo = (function(cd, $) {
2525
const $trafficLightSummary = (light, kataId) => {
2626
const $tr = $('<tr>');
2727
$tr.append($trafficLightIndexTd(light));
28-
$tr.append($trafficLightImageTd(light));
2928
$tr.append($('<td class="mini-text">').html(miniTextInfo(kataId, light)));
3029
return $('<table>').append($tr);
3130
};
@@ -37,14 +36,6 @@ var cyberDojo = (function(cd, $) {
3736
return $('<td>').append($count);
3837
};
3938

40-
const $trafficLightImageTd = (light) => {
41-
const $img = $('<img>', {
42-
src:`/images/traffic-light/${light.colour}.png`,
43-
class:'diff-hover-tip'
44-
});
45-
return $('<td>').append($img);
46-
};
47-
4839
const miniTextInfo = (kataId, light) => {
4940
switch (light.colour) {
5041
case 'create': return 'Kata created';
@@ -122,7 +113,6 @@ var cyberDojo = (function(cd, $) {
122113
const $tr = $('<tr>');
123114
$tr.append($lineCountTd('deleted', fileDiff));
124115
$tr.append($lineCountTd('added', fileDiff));
125-
$tr.append($diffTypeTd(fileDiff));
126116
$tr.append($diffFilenameTd(fileDiff));
127117
$table.append($tr);
128118
}
@@ -145,17 +135,12 @@ var cyberDojo = (function(cd, $) {
145135
return $('<td>').append($count);
146136
};
147137

148-
const $diffTypeTd = (diff) => {
149-
const $type = $('<div>', {
150-
class:`hover diff-type-marker ${diff.type}`
151-
});
152-
return $('<td>').append($type);
153-
};
154-
155138
const $diffFilenameTd = (diff) => {
156-
const $filename = $('<div>', { class:`hover diff-filename ${diff.type}` });
157-
$filename.text(diffFilename(diff));
158-
return $('<td>').append($filename);
139+
const filename = diffFilename(diff);
140+
const fileType = cd.isTestFile(filename) ? 'test-file' : 'non-test-file';
141+
const $filename = $('<div>', { class:`hover diff-filename ${diff.type} ${fileType}` });
142+
$filename.text(filename);
143+
return $('<td>', { style:'padding-left:4px' }).append($filename);
159144
};
160145

161146
const diffFilename = (diff) => {

source/app/assets/javascripts/cyber-dojo_lib_append_traffic_light.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@ var cyberDojo = ((cd, $) => {
6262

6363
// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6464
const $fileEventImage = (event) => {
65+
const filename = event.colour === 'file_rename' ? event.new_filename : event.filename;
66+
const icon = cd.isTestFile(filename) ? 'file_test' : 'file_code';
6567
return $('<img>', {
6668
class: 'diff-traffic-light',
67-
src: `/images/traffic-light/${event.colour}.png`,
68-
alt: `${event.colour} traffic-light`,
69+
src: `/images/traffic-light/${icon}.png`,
70+
alt: icon,
6971
'data-colour': event.colour,
7072
'data-index': event.index
7173
});

source/app/assets/stylesheets/hover-tip.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ $hoverFadePercent: 5%;
2222
width: auto; min-width: 0; // reset width
2323
}
2424

25+
.hover-tip .diff-filename.test-file { color: steelblue; }
26+
.hover-tip .diff-filename.non-test-file { color: white; }
27+
2528
.hover-tip .mini-text
2629
{
2730
color: lighten(Gray,10%);

source/app/views/kata/_file_create_rename_delete.erb

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ $(() => {
2424

2525
const currentFilename = () => kata.tabs.filename().text();
2626

27-
cd.createFileButton().click(() => openCreateFileDialog());
28-
cd.deleteFileButton().click(() => openDeleteFileDialog());
29-
cd.renameFileButton().click(() => openRenameFileDialog());
27+
cd.createFileButton().click(() => cd.waitForITE(openCreateFileDialog));
28+
cd.deleteFileButton().click(() => cd.waitForITE(openDeleteFileDialog));
29+
cd.renameFileButton().click(() => cd.waitForITE(openRenameFileDialog));
3030

3131
if (!cd.kata.isLatest()) {
3232
cd.createFileButton().prop('disabled', true);
@@ -117,10 +117,14 @@ $(() => {
117117
$(dialog).on('close', () => { dialog.remove(); cd.kata.editor.refocus(); });
118118
$('.dialog-close', dialog).click(() => dialog.close());
119119

120-
input.keyup((event) => {
120+
input.keyup(() => {
121121
const newFilename = $.trim(input.val());
122122
$okButton.prop('disabled', !isValidFilename(newFilename));
123-
if (!$okButton.prop('disabled') && event.key === 'Enter') {
123+
});
124+
125+
$(dialog).keydown((event) => {
126+
if (event.key === 'Enter' && !$okButton.prop('disabled')) {
127+
event.preventDefault();
124128
$okButton.click();
125129
}
126130
});
@@ -132,6 +136,14 @@ $(() => {
132136

133137
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
134138
const filenameRange = (f) => {
139+
// Returns [lo,hi] for setSelectionRange, pre-selecting the meaningful part
140+
// of the filename in the create/rename dialog. Narrows the selection by:
141+
// 1. Stripping the extension (.py, .js etc) from the right.
142+
// 2. Stripping any leading directory path (src/ test/ etc) from the left.
143+
// 3. Stripping any test word (test/tests/spec/steps) plus an adjacent
144+
// separator (. _ - or nothing) from either end of what remains.
145+
// eg src/hiker_test.py -> selects only "hiker".
146+
135147
let lo = 0;
136148
// remove trailing .extension
137149
const ext = f.lastIndexOf('.');

source/app/views/kata/_file_inter_test_events.erb

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,29 @@ document.addEventListener('DOMContentLoaded', () => {
44

55
const kata = cd.kata;
66

7+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
8+
9+
let _interTestEventInProgress = false;
10+
11+
cd.interTestEventInProgress = () => _interTestEventInProgress;
12+
13+
cd.waitForITE = (callback) => {
14+
if (!_interTestEventInProgress) {
15+
callback();
16+
return;
17+
}
18+
const pollInterval = 50;
19+
const maxWait = 2000;
20+
let elapsed = 0;
21+
const poll = setInterval(() => {
22+
elapsed += pollInterval;
23+
if (!_interTestEventInProgress || elapsed >= maxWait) {
24+
clearInterval(poll);
25+
callback();
26+
}
27+
}, pollInterval);
28+
};
29+
730
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
831
// Inter-Test-Events
932
// Called in many places, eg when a file is created, deleted, renamed,
@@ -30,8 +53,12 @@ document.addEventListener('DOMContentLoaded', () => {
3053

3154
cd.saveAnyFileChangesITE = (callback) => {
3255
cd.saveCodeFromSyntaxHighlightEditors();
56+
_interTestEventInProgress = true;
3357
const args = { id: kata.id, index: kata.index };
34-
syncPostWithCallbackITE('/kata/file_edit', args, callback);
58+
syncPostWithCallbackITE('/kata/file_edit', args, () => {
59+
_interTestEventInProgress = false;
60+
callback();
61+
});
3562
};
3663

3764
const syncPostWithCallbackITE = (url, args, callback) => {

source/app/views/kata/_filenames.erb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,14 @@ $(() => {
125125
$div.addClass('source');
126126
}
127127
$div.click(() => {
128+
cd.setFilesEditable(false);
129+
try {
130+
cd.saveAnyFileChangesITE(() => { cd.setFilesEditable(true); });
131+
} catch(e) {
132+
cd.setFilesEditable(true);
133+
throw e;
134+
}
128135
filenames.select(filename);
129-
cd.saveAnyFileChangesITE(() => {});
130136
});
131137
return $div;
132138
};

source/app/views/kata/_test_button.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ $(() => {
88

99
const $button = $('#test-button');
1010

11-
$button.click(() => kata.testButton.click());
11+
$button.click(() => cd.waitForITE(() => kata.testButton.click()));
1212

1313
kata.testButton = {
1414
hide: (f) => {

source/app/views/shared/_sorted_filenames.erb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ $(() => {
3838
filename != 'cyber-dojo.sh';
3939
};
4040

41+
cd.isTestFile = (filename) => {
42+
// True if the stem starts or ends with a test word (tests/test/spec/steps).
43+
// Middle-only matches (eg my_test_helper) do not count.
44+
const testWords = ['tests', 'test', 'spec', 'steps'];
45+
const ext = filename.lastIndexOf('.');
46+
const hi = (ext !== -1) ? ext : filename.length;
47+
const stem = filename.substring(0, hi).toLowerCase();
48+
const base = stem.substring(stem.lastIndexOf('/') + 1);
49+
return testWords.some(word => base.startsWith(word) || base.endsWith(word));
50+
};
51+
4152
cd.highlightFilenames = () => {
4253
const manifest = cd.kata.manifest();
4354
return manifest.highlight_filenames;

0 commit comments

Comments
 (0)