[FIX] web: CP concurence issue with custom delay
Commit 62ebc97bd8 introduced the
possibility to customise the debounce delay associated with a command
palette search based on namespace. This introduced a concurrency bug
when changing namespace.
Problem:
Let's say we have 2 namespaces with very different debounce delays.
For example, # has 2000 and @ has 0.
1. A user enter "#"
2. He changes his mind before the end of the search. He deletes "#"
and enter "@".
3. The search associated with "@" is resolved before the search for "#".
The associated results are displayed.
4. The search associated with "#" is resolved. The associated results
are displayed.
5. The user does not get the results related to his last search which
are those of "@".
Solution:
When we change the namespace, we will cancel the current search function.
Part-of: odoo/odoo#82773
This commit is contained in:
@@ -271,20 +271,18 @@ export class CommandPalette extends Component {
|
||||
|
||||
debounceSearch(value) {
|
||||
const { namespace } = this.processSearchValue(value);
|
||||
if (!this.debounceSearchByNamespace[namespace]) {
|
||||
const namespaceConfig = this.configByNamespace[namespace] || {};
|
||||
if (namespaceConfig.debounceDelay) {
|
||||
this.debounceSearchByNamespace[namespace] = debounce.apply(this, [
|
||||
this.search,
|
||||
namespaceConfig.debounceDelay,
|
||||
]);
|
||||
} else {
|
||||
this.debounceSearchByNamespace[namespace] = this.defaultDebounceSearch;
|
||||
const namespaceConfig = this.configByNamespace[namespace] || {};
|
||||
if (this.namespace !== namespace) {
|
||||
if (this.lastDebounceSearch) {
|
||||
this.lastDebounceSearch.cancel();
|
||||
}
|
||||
this.lastDebounceSearch = debounce(
|
||||
(value) => this.search(value),
|
||||
namespaceConfig.debounceDelay || 0
|
||||
);
|
||||
this.namespace = namespace;
|
||||
}
|
||||
const debounceSearch =
|
||||
this.debounceSearchByNamespace[namespace] || this.defaultDebounceSearch;
|
||||
this.searchValuePromise = debounceSearch.apply(this, [value]).catch(() => {
|
||||
this.searchValuePromise = this.lastDebounceSearch(value).catch(() => {
|
||||
this.searchValuePromise = null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -181,6 +181,78 @@ QUnit.test("custom debounce delay", async (assert) => {
|
||||
assert.verifySteps(["0", "200", "100", "0"]);
|
||||
});
|
||||
|
||||
QUnit.test("concurrency with custom debounce delay", async (assert) => {
|
||||
const def = makeDeferred();
|
||||
patchWithCleanup(browser, {
|
||||
clearTimeout: () => {},
|
||||
setTimeout: async (later, delay) => {
|
||||
if (delay === 200) {
|
||||
await def;
|
||||
}
|
||||
later();
|
||||
},
|
||||
});
|
||||
|
||||
await mount(TestComponent, target, { env });
|
||||
const configByNamespace = {
|
||||
"@": {
|
||||
debounceDelay: 200,
|
||||
},
|
||||
"#": {
|
||||
debounceDelay: 100,
|
||||
},
|
||||
};
|
||||
const action = () => {};
|
||||
const providers = [
|
||||
{
|
||||
namespace: "@",
|
||||
provide: () => [
|
||||
{
|
||||
name: "Command@",
|
||||
action,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
namespace: "#",
|
||||
provide: () => [
|
||||
{
|
||||
name: "Command#",
|
||||
action,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const config = {
|
||||
configByNamespace,
|
||||
providers,
|
||||
};
|
||||
env.services.dialog.add(CommandPaletteDialog, {
|
||||
config,
|
||||
});
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_command_palette");
|
||||
assert.containsNone(target, ".o_command");
|
||||
await editSearchBar("@");
|
||||
await nextTick();
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_command")].map((el) => el.textContent),
|
||||
[]
|
||||
);
|
||||
await editSearchBar("#");
|
||||
await nextTick();
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_command")].map((el) => el.textContent),
|
||||
["Command#"]
|
||||
);
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_command")].map((el) => el.textContent),
|
||||
["Command#"]
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("custom placeholder", async (assert) => {
|
||||
await mount(TestComponent, target, { env });
|
||||
const config = {
|
||||
|
||||
Reference in New Issue
Block a user