feat: advance legacy UI behavior parity
This commit is contained in:
@@ -10,6 +10,7 @@ const webRoot = path.join(repositoryRoot, "src", "MBN_STOCK_WEBVIEW.LegacyParity
|
||||
const app = fs.readFileSync(path.join(webRoot, "app.js"), "utf8");
|
||||
const playout = fs.readFileSync(path.join(webRoot, "playout-ui.js"), "utf8");
|
||||
const markup = fs.readFileSync(path.join(webRoot, "index.html"), "utf8");
|
||||
const styles = fs.readFileSync(path.join(webRoot, "styles.css"), "utf8");
|
||||
|
||||
test("legacy shell exposes the original stock, cut, playlist and playout controls", () => {
|
||||
for (const id of [
|
||||
@@ -182,7 +183,11 @@ test("legacy child modal Escape is inert and cannot leak into the playout listen
|
||||
"modalFocusManager",
|
||||
"cutDragGesture",
|
||||
"clearCutDragGesture",
|
||||
"playlistDragGesture",
|
||||
"clearPlaylistDragGesture",
|
||||
"deferredPlaylistPlainSelectionRowId",
|
||||
"legacyAlertDialog",
|
||||
"legacyDialogIsConfirmation",
|
||||
"send",
|
||||
app.slice(appHandlerBodyStart, appHandlerEnd));
|
||||
|
||||
@@ -238,7 +243,11 @@ test("legacy child modal Escape is inert and cannot leak into the playout listen
|
||||
{ getActiveModal() { return childDialog; } },
|
||||
null,
|
||||
() => {},
|
||||
null,
|
||||
() => {},
|
||||
null,
|
||||
legacyAlertDialog,
|
||||
false,
|
||||
(type) => sent.push(type));
|
||||
assert.equal(modal.hidden, false);
|
||||
assert.equal(escape.defaultPrevented, true);
|
||||
@@ -263,13 +272,17 @@ test("legacy child modal Escape is inert and cannot leak into the playout listen
|
||||
{ getActiveModal() { return legacyAlertDialog; } },
|
||||
null,
|
||||
() => {},
|
||||
null,
|
||||
() => {},
|
||||
null,
|
||||
legacyAlertDialog,
|
||||
false,
|
||||
(type) => sent.push(type));
|
||||
assert.equal(alertEscape.defaultPrevented, true);
|
||||
assert.deepEqual(sent, ["dismiss-dialog"]);
|
||||
});
|
||||
|
||||
test("started cut drag owns playout keys and Escape cancels without TAKE OUT", () => {
|
||||
test("started pointer drag owns playout keys and Escape cancels without TAKE OUT", () => {
|
||||
const keydownMarker = 'document.addEventListener("keydown", function (event) {';
|
||||
const start = app.lastIndexOf(keydownMarker) + keydownMarker.length;
|
||||
const end = app.indexOf("\n });", start);
|
||||
@@ -278,7 +291,11 @@ test("started cut drag owns playout keys and Escape cancels without TAKE OUT", (
|
||||
"modalFocusManager",
|
||||
"cutDragGesture",
|
||||
"clearCutDragGesture",
|
||||
"playlistDragGesture",
|
||||
"clearPlaylistDragGesture",
|
||||
"deferredPlaylistPlainSelectionRowId",
|
||||
"legacyAlertDialog",
|
||||
"legacyDialogIsConfirmation",
|
||||
"send",
|
||||
app.slice(start, end));
|
||||
const sent = [];
|
||||
@@ -296,11 +313,36 @@ test("started cut drag owns playout keys and Escape cancels without TAKE OUT", (
|
||||
manager,
|
||||
{ started: true },
|
||||
() => { clears += 1; },
|
||||
null,
|
||||
() => {},
|
||||
null,
|
||||
{},
|
||||
false,
|
||||
(type) => sent.push(type));
|
||||
assert.equal(event.defaultPrevented, true, key + " must stay inside drag loop");
|
||||
}
|
||||
assert.equal(clears, 1, "only Escape cancels the active drag");
|
||||
|
||||
const playlistEscape = {
|
||||
key: "Escape",
|
||||
repeat: false,
|
||||
defaultPrevented: false,
|
||||
preventDefault() { this.defaultPrevented = true; }
|
||||
};
|
||||
let playlistClears = 0;
|
||||
handle(
|
||||
playlistEscape,
|
||||
manager,
|
||||
null,
|
||||
() => {},
|
||||
{ started: true },
|
||||
() => { playlistClears += 1; },
|
||||
"row-a",
|
||||
{},
|
||||
false,
|
||||
(type) => sent.push(type));
|
||||
assert.equal(playlistEscape.defaultPrevented, true);
|
||||
assert.equal(playlistClears, 1);
|
||||
assert.deepEqual(sent, []);
|
||||
});
|
||||
|
||||
@@ -359,7 +401,12 @@ test("playlist reorder reconciles keyed DOM nodes without discarding focus", ()
|
||||
assert.doesNotMatch(app, /playlistRows\.replaceChild/);
|
||||
});
|
||||
|
||||
test("playlist native drag sends one source row and rejects checkbox drag", () => {
|
||||
test("playlist reorder starts only from the 30 px row header pointer handle", () => {
|
||||
assert.match(markup,
|
||||
/playlist-header playlist-row">\s*<div aria-hidden="true"><\/div><div>송출<\/div>/u);
|
||||
assert.match(styles,
|
||||
/\.playlist-row\s*\{[^}]*grid-template-columns:\s*30px 40px 100px/u);
|
||||
assert.match(styles, /\.playlist-data-row \.playlist-row-header/u);
|
||||
class MiniClassList {
|
||||
constructor() { this.names = new Set(); }
|
||||
add(...names) { names.forEach((name) => this.names.add(name)); }
|
||||
@@ -381,6 +428,8 @@ test("playlist native drag sends one source row and rejects checkbox drag", () =
|
||||
this.attributes = new Map();
|
||||
this.classList = new MiniClassList();
|
||||
this.listeners = new Map();
|
||||
this.bounds = { left: 0, top: 0, width: 30, height: 20, right: 30, bottom: 20 };
|
||||
this.capturedPointerId = null;
|
||||
}
|
||||
|
||||
set className(value) {
|
||||
@@ -399,6 +448,12 @@ test("playlist native drag sends one source row and rejects checkbox drag", () =
|
||||
return candidate === this || this.children.some((child) => child.contains(candidate));
|
||||
}
|
||||
closest(selector) {
|
||||
if (selector.startsWith(".")) {
|
||||
const className = selector.slice(1);
|
||||
return this.classList.contains(className)
|
||||
? this
|
||||
: this.parentElement?.closest(selector) || null;
|
||||
}
|
||||
const tags = selector.split(",").map((value) => value.trim().toUpperCase());
|
||||
return tags.includes(this.tagName)
|
||||
? this
|
||||
@@ -415,9 +470,25 @@ test("playlist native drag sends one source row and rejects checkbox drag", () =
|
||||
listener.call(this, event);
|
||||
}
|
||||
}
|
||||
getBoundingClientRect() { return { top: 0, height: 20 }; }
|
||||
getBoundingClientRect() { return this.bounds; }
|
||||
setPointerCapture(pointerId) { this.capturedPointerId = pointerId; }
|
||||
hasPointerCapture(pointerId) { return this.capturedPointerId === pointerId; }
|
||||
releasePointerCapture(pointerId) {
|
||||
if (this.capturedPointerId === pointerId) this.capturedPointerId = null;
|
||||
}
|
||||
setAttribute(name, value) { this.attributes.set(name, String(value)); }
|
||||
getAttribute(name) { return this.attributes.get(name) ?? null; }
|
||||
querySelector(selector) {
|
||||
const descendants = this.querySelectorAll("*");
|
||||
if (selector === "input") {
|
||||
return descendants.find((element) => element.tagName === "INPUT") || null;
|
||||
}
|
||||
if (selector.startsWith(".")) {
|
||||
return descendants.find((element) =>
|
||||
element.classList.contains(selector.slice(1))) || null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
querySelectorAll(selector) {
|
||||
const descendants = [];
|
||||
const visit = (element) => {
|
||||
@@ -427,6 +498,7 @@ test("playlist native drag sends one source row and rejects checkbox drag", () =
|
||||
});
|
||||
};
|
||||
visit(this);
|
||||
if (selector === "*") return descendants;
|
||||
if (selector === '.playlist-data-row[aria-selected="true"]') {
|
||||
return descendants.filter((element) =>
|
||||
element.classList.contains("playlist-data-row") &&
|
||||
@@ -441,20 +513,14 @@ test("playlist native drag sends one source row and rejects checkbox drag", () =
|
||||
}
|
||||
}
|
||||
|
||||
class MiniDataTransfer {
|
||||
constructor() { this.values = new Map(); }
|
||||
setData(type, value) { this.values.set(type, value); }
|
||||
getData(type) { return this.values.get(type) || ""; }
|
||||
}
|
||||
|
||||
const sourceStart = app.indexOf("function clearPlaylistDragVisuals");
|
||||
const sourceEnd = app.indexOf("function renderPlaylist", sourceStart);
|
||||
assert.ok(sourceStart >= 0 && sourceEnd > sourceStart);
|
||||
const createHarness = new Function("document", "playlistRows", "send", "playlistDragMime", `
|
||||
let draggedPlaylistRowId = null;
|
||||
const createHarness = new Function("document", "playlistRows", "send", "window", `
|
||||
let deferredPlaylistPlainSelectionRowId = null;
|
||||
let playlistDragBlockedRowId = null;
|
||||
let playlistDragGesture = null;
|
||||
let playlistMutationLocked = false;
|
||||
let cutDragSize = { width: 4, height: 4 };
|
||||
function dragDropPosition(event, element) {
|
||||
const bounds = element.getBoundingClientRect();
|
||||
return event.clientY < bounds.top + bounds.height / 2 ? "before" : "after";
|
||||
@@ -462,29 +528,50 @@ test("playlist native drag sends one source row and rejects checkbox drag", () =
|
||||
${app.slice(sourceStart, sourceEnd)}
|
||||
return {
|
||||
createPlaylistElement,
|
||||
onPlaylistDragStart,
|
||||
state: () => ({ draggedPlaylistRowId, deferredPlaylistPlainSelectionRowId })
|
||||
state: () => ({ playlistDragGesture, deferredPlaylistPlainSelectionRowId })
|
||||
};
|
||||
`);
|
||||
const document = { createElement: (tagName) => new MiniElement(tagName) };
|
||||
let pointedElement = null;
|
||||
const document = {
|
||||
createElement: (tagName) => new MiniElement(tagName),
|
||||
elementFromPoint: () => pointedElement
|
||||
};
|
||||
const playlistRows = new MiniElement("div");
|
||||
playlistRows.bounds = { left: 0, top: 0, width: 500, height: 100,
|
||||
right: 500, bottom: 100 };
|
||||
const sent = [];
|
||||
const legacyCutDrag = require(path.join(webRoot, "legacy-cut-drag.js"));
|
||||
const harness = createHarness(
|
||||
document, playlistRows, (type, payload) => sent.push({ type, payload }),
|
||||
"text/x-mbn-playlist-row");
|
||||
{ LegacyCutDrag: legacyCutDrag });
|
||||
const source = harness.createPlaylistElement({ rowId: "row-a" });
|
||||
source.bounds = { left: 0, top: 0, width: 500, height: 20, right: 500, bottom: 20 };
|
||||
source.setAttribute("aria-selected", "true");
|
||||
playlistRows.appendChild(source);
|
||||
const selectedPeer = harness.createPlaylistElement({ rowId: "row-b" });
|
||||
selectedPeer.bounds = { left: 0, top: 20, width: 500, height: 20,
|
||||
right: 500, bottom: 40 };
|
||||
selectedPeer.setAttribute("aria-selected", "true");
|
||||
playlistRows.appendChild(selectedPeer);
|
||||
const target = harness.createPlaylistElement({ rowId: "row-c" });
|
||||
target.bounds = { left: 0, top: 40, width: 500, height: 20,
|
||||
right: 500, bottom: 60 };
|
||||
target.setAttribute("aria-selected", "false");
|
||||
playlistRows.appendChild(target);
|
||||
|
||||
const checkbox = source.children[0].children[0];
|
||||
const blockedTransfer = new MiniDataTransfer();
|
||||
let checkboxDragPrevented = false;
|
||||
const sourceHeader = source.children[0];
|
||||
const checkbox = source.children[1].children[0];
|
||||
const targetHeader = target.children[0];
|
||||
sourceHeader.bounds = { left: 0, top: 0, width: 30, height: 20,
|
||||
right: 30, bottom: 20 };
|
||||
targetHeader.bounds = { left: 0, top: 40, width: 30, height: 20,
|
||||
right: 30, bottom: 60 };
|
||||
|
||||
assert.equal(source.draggable, false);
|
||||
assert.equal(checkbox.draggable, false);
|
||||
assert.equal(source.listeners.has("pointermove"), false);
|
||||
assert.equal(sourceHeader.listeners.has("pointermove"), true);
|
||||
|
||||
source.invoke("pointerdown", {
|
||||
button: 0,
|
||||
currentTarget: source,
|
||||
@@ -497,44 +584,76 @@ test("playlist native drag sends one source row and rejects checkbox drag", () =
|
||||
payload: { rowId: "row-a" }
|
||||
}]);
|
||||
sent.length = 0;
|
||||
harness.onPlaylistDragStart({
|
||||
currentTarget: source,
|
||||
// Chromium reports the draggable row as dragstart.target even though
|
||||
// pointerdown originated on the checkbox.
|
||||
target: source,
|
||||
dataTransfer: blockedTransfer,
|
||||
preventDefault: () => { checkboxDragPrevented = true; }
|
||||
});
|
||||
assert.equal(checkboxDragPrevented, true);
|
||||
assert.equal(blockedTransfer.getData("text/x-mbn-playlist-row"), "");
|
||||
assert.equal(harness.state().playlistDragGesture, null);
|
||||
|
||||
const pointerDown = new Event("pointerdown", { cancelable: true });
|
||||
Object.assign(pointerDown, { button: 0, ctrlKey: false, shiftKey: false });
|
||||
source.dispatchEvent(pointerDown);
|
||||
const dataPointerDown = {
|
||||
button: 0, pointerId: 1, clientX: 100, clientY: 10,
|
||||
ctrlKey: false, shiftKey: false, target: source, currentTarget: source
|
||||
};
|
||||
source.invoke("pointerdown", dataPointerDown);
|
||||
assert.equal(harness.state().deferredPlaylistPlainSelectionRowId, "row-a");
|
||||
assert.equal(harness.state().playlistDragGesture, null);
|
||||
source.invoke("pointerup", { ...dataPointerDown, button: 0 });
|
||||
assert.deepEqual(sent, [{
|
||||
type: "select-playlist-row",
|
||||
payload: { rowId: "row-a", control: false, shift: false }
|
||||
}]);
|
||||
sent.length = 0;
|
||||
|
||||
const pointerDown = {
|
||||
button: 0, pointerId: 7, clientX: 10, clientY: 10,
|
||||
ctrlKey: false, shiftKey: false, target: sourceHeader,
|
||||
stopPropagation() {}
|
||||
};
|
||||
sourceHeader.invoke("pointerdown", { ...pointerDown, currentTarget: sourceHeader });
|
||||
assert.deepEqual(sent, [{
|
||||
type: "activate-playlist-row",
|
||||
payload: { rowId: "row-a" }
|
||||
}]);
|
||||
sent.length = 0;
|
||||
assert.equal(sourceHeader.hasPointerCapture(7), true);
|
||||
assert.equal(harness.state().playlistDragGesture.started, false);
|
||||
|
||||
// Crossing the system drag rectangle over a normal data cell starts the
|
||||
// gesture but cannot establish a drop target.
|
||||
pointedElement = target.children[2];
|
||||
sourceHeader.invoke("pointermove", {
|
||||
pointerId: 7, buttons: 1, clientX: 100, clientY: 45,
|
||||
currentTarget: sourceHeader, preventDefault() {}
|
||||
});
|
||||
assert.equal(harness.state().playlistDragGesture.started, true);
|
||||
assert.equal(harness.state().playlistDragGesture.targetRowId, null);
|
||||
sourceHeader.invoke("pointerup", {
|
||||
pointerId: 7, button: 0, clientX: 100, clientY: 45,
|
||||
currentTarget: sourceHeader, preventDefault() {}
|
||||
});
|
||||
assert.deepEqual(sent, []);
|
||||
|
||||
const transfer = new MiniDataTransfer();
|
||||
const dragStart = new Event("dragstart", { cancelable: true });
|
||||
Object.assign(dragStart, { dataTransfer: transfer });
|
||||
source.dispatchEvent(dragStart);
|
||||
assert.equal(harness.state().deferredPlaylistPlainSelectionRowId, null);
|
||||
assert.equal(transfer.getData("text/x-mbn-playlist-row"), "row-a");
|
||||
assert.deepEqual(sent, []);
|
||||
|
||||
const dragOver = new Event("dragover", { cancelable: true });
|
||||
Object.assign(dragOver, { dataTransfer: transfer, clientY: 1 });
|
||||
target.dispatchEvent(dragOver);
|
||||
assert.equal(dragOver.defaultPrevented, true);
|
||||
|
||||
const drop = new Event("drop", { cancelable: true });
|
||||
Object.assign(drop, { dataTransfer: transfer, clientY: 1 });
|
||||
target.dispatchEvent(drop);
|
||||
// A second gesture dropped on another row's 30 px header sends the existing
|
||||
// stable-row C# contract exactly once.
|
||||
sourceHeader.invoke("pointerdown", { ...pointerDown, pointerId: 8,
|
||||
currentTarget: sourceHeader });
|
||||
assert.deepEqual(sent, [{
|
||||
type: "activate-playlist-row",
|
||||
payload: { rowId: "row-a" }
|
||||
}]);
|
||||
sent.length = 0;
|
||||
pointedElement = targetHeader;
|
||||
sourceHeader.invoke("pointermove", {
|
||||
pointerId: 8, buttons: 1, clientX: 10, clientY: 45,
|
||||
currentTarget: sourceHeader, preventDefault() {}
|
||||
});
|
||||
sourceHeader.invoke("pointerup", {
|
||||
pointerId: 8, button: 0, clientX: 10, clientY: 45,
|
||||
currentTarget: sourceHeader, preventDefault() {}
|
||||
});
|
||||
assert.deepEqual(sent, [{
|
||||
type: "reorder-playlist-rows",
|
||||
payload: { sourceRowId: "row-a", targetRowId: "row-c", position: "before" }
|
||||
}]);
|
||||
assert.equal(Object.hasOwn(sent[0].payload, "selectedRowIds"), false);
|
||||
assert.equal(sourceHeader.hasPointerCapture(8), false);
|
||||
assert.equal(harness.state().playlistDragGesture, null);
|
||||
});
|
||||
|
||||
test("cut drag uses the original pointer rectangle and drops only inside the playlist", () => {
|
||||
@@ -1077,13 +1196,96 @@ test("fixed-262 ignores a single click and emits exactly one intent on double cl
|
||||
});
|
||||
|
||||
test("industry workspace sends index and opaque action intents only", () => {
|
||||
const renderStart = app.indexOf("function renderIndustry");
|
||||
const renderEnd = app.indexOf("function renderOverseas", renderStart);
|
||||
const industryRender = app.slice(renderStart, renderEnd);
|
||||
assert.match(app, /state\.industry/);
|
||||
assert.match(app, /dataset\.industryIndex/);
|
||||
assert.match(app, /dataset\.industryActionId/);
|
||||
assert.match(app, /send\("select-industry"/);
|
||||
assert.match(app, /send\("double-click-industry"/);
|
||||
assert.match(app, /send\("swap-industries"/);
|
||||
assert.match(app, /send\("set-industry-comparison-text"/);
|
||||
assert.match(app, /send\("activate-industry-action"/);
|
||||
assert.doesNotMatch(app, /swap\.textContent = "↕ 교환"/);
|
||||
assert.match(app,
|
||||
/summary\.textContent = industry\.market === "kospi" \? "\[코스피\]" : "\[코스닥\]"/);
|
||||
assert.match(app, /summary\.dataset\.industrySectionIndex = "0"/);
|
||||
assert.doesNotMatch(app, /const sections = new Map\(\)/);
|
||||
assert.doesNotMatch(industryRender, /first\.readOnly = true/);
|
||||
assert.doesNotMatch(industryRender, /second\.readOnly = true/);
|
||||
assert.match(industryRender, /first\.maxLength = 128/);
|
||||
assert.match(industryRender, /second\.maxLength = 128/);
|
||||
});
|
||||
|
||||
test("UC2 row selection is immediate and its state receipt preserves the double-click target", () => {
|
||||
const helperStart = app.indexOf("function markIndustryRowSelected");
|
||||
const helperEnd = app.indexOf("function renderIndustry", helperStart);
|
||||
assert.ok(helperStart >= 0 && helperEnd > helperStart);
|
||||
|
||||
function row(index) {
|
||||
return {
|
||||
id: `industry-row-${index}`,
|
||||
dataset: { industryIndex: String(index) },
|
||||
list: null,
|
||||
selected: false,
|
||||
classList: {
|
||||
toggle(_name, selected) { this.owner.selected = selected; },
|
||||
owner: null
|
||||
},
|
||||
setAttribute(name, value) {
|
||||
if (name === "aria-selected") this.ariaSelected = value;
|
||||
},
|
||||
closest(selector) {
|
||||
return selector === ".industry-list" ? this.list : null;
|
||||
}
|
||||
};
|
||||
}
|
||||
const first = row(0);
|
||||
const second = row(1);
|
||||
first.classList.owner = first;
|
||||
second.classList.owner = second;
|
||||
const categoryTree = {
|
||||
querySelectorAll() { return [first, second]; }
|
||||
};
|
||||
const industryList = {
|
||||
focus(options) { this.focusOptions = options; },
|
||||
setAttribute(name, value) {
|
||||
if (name === "aria-activedescendant") this.activeDescendant = value;
|
||||
}
|
||||
};
|
||||
first.list = industryList;
|
||||
second.list = industryList;
|
||||
const sent = [];
|
||||
const helpers = new Function(
|
||||
"categoryTree",
|
||||
"send",
|
||||
`
|
||||
${app.slice(helperStart, helperEnd)}
|
||||
return { selectIndustryRow, activateIndustryRow };
|
||||
`)(
|
||||
categoryTree,
|
||||
(type, payload) => sent.push({ type, payload }));
|
||||
|
||||
helpers.selectIndustryRow(second);
|
||||
assert.equal(second.selected, true);
|
||||
assert.equal(first.selected, false);
|
||||
assert.deepEqual(sent, [
|
||||
{ type: "select-industry", payload: { index: 1 } }
|
||||
]);
|
||||
helpers.activateIndustryRow(second);
|
||||
assert.deepEqual(sent[1], {
|
||||
type: "double-click-industry", payload: { index: 1 }
|
||||
});
|
||||
|
||||
const renderStart = app.indexOf("function renderIndustry");
|
||||
const renderEnd = app.indexOf("function renderOverseas", renderStart);
|
||||
const render = app.slice(renderStart, renderEnd);
|
||||
assert.match(render, /structureKey === industryRenderStructureKey/);
|
||||
assert.match(render, /existingWorkspace\.querySelectorAll/);
|
||||
assert.match(render, /previousList\.scrollTop/);
|
||||
assert.match(render, /list\.scrollTop = previousScrollTop/);
|
||||
assert.match(render, /replacement\.focus\(\{ preventScroll: true \}\)/);
|
||||
});
|
||||
|
||||
test("comparison workspace sends opaque target, pair and action intents", () => {
|
||||
@@ -1101,6 +1303,13 @@ test("comparison workspace sends opaque target, pair and action intents", () =>
|
||||
assert.match(app, /dataset\.comparisonSelectionId/);
|
||||
assert.match(app, /dataset\.comparisonPairId/);
|
||||
assert.match(app, /dataset\.comparisonActionId/);
|
||||
for (const label of ["◀▶", "추가", "UP", "DOWN", "DEL", "DEL ALL"]) {
|
||||
assert.match(app, new RegExp("\\\"" + label + "\\\""));
|
||||
}
|
||||
assert.doesNotMatch(app, /clear\.dataset\.comparisonClear/);
|
||||
assert.doesNotMatch(app, /window\.confirm\("저장된 종목 비교쌍을 모두 삭제하겠습니까\?"\)/);
|
||||
assert.match(app, /\? "confirm-native-dialog"/);
|
||||
assert.match(app, /send\("cancel-native-dialog", \{\}\)/);
|
||||
assert.doesNotMatch(app, /send\([^\n]*stockCode/);
|
||||
});
|
||||
|
||||
@@ -1140,7 +1349,7 @@ test("theme expert and trading halt actions preserve original single click wirin
|
||||
assert.doesNotMatch(doubleClickDelegate, new RegExp("send\\(\\\"" + intent + "\\\""));
|
||||
assert.equal(app.split('send("' + intent + '"').length - 1, 1);
|
||||
}
|
||||
assert.match(doubleClickDelegate, /send\("double-click-industry"/);
|
||||
assert.match(doubleClickDelegate, /activateIndustryRow\(industryRow\)/);
|
||||
for (const intent of [
|
||||
"choose-comparison-domestic", "choose-comparison-world", "choose-comparison-fixed"
|
||||
]) {
|
||||
|
||||
Reference in New Issue
Block a user