274 lines
8.8 KiB
JavaScript
274 lines
8.8 KiB
JavaScript
import KnowledgeHierarchy from "@knowledge/components/hierarchy/hierarchy";
|
|
|
|
import { registry } from "@web/core/registry";
|
|
import { Component, xml } from "@odoo/owl";
|
|
import { standardWidgetProps } from "@web/views/widgets/standard_widget_props";
|
|
|
|
import { defineMailModels } from "@mail/../tests/mail_test_helpers";
|
|
import { after, before, expect, test } from "@odoo/hoot";
|
|
import { click, queryFirst } from "@odoo/hoot-dom";
|
|
import { animationFrame } from "@odoo/hoot-mock";
|
|
import {
|
|
defineModels,
|
|
fields,
|
|
MockServer,
|
|
models,
|
|
mountView,
|
|
onRpc,
|
|
} from "@web/../tests/web_test_helpers";
|
|
|
|
class Article extends models.Model {
|
|
name = fields.Char();
|
|
icon = fields.Char();
|
|
display_name = fields.Char();
|
|
parent_id = fields.Many2one({ relation: "article" });
|
|
root_article_id = fields.Many2one({ relation: "article" });
|
|
parent_path = fields.Char();
|
|
is_locked = fields.Boolean();
|
|
user_can_write = fields.Boolean();
|
|
user_has_access = fields.Boolean();
|
|
category = fields.Char();
|
|
|
|
_records = [
|
|
{
|
|
id: 1,
|
|
name: "Root Article",
|
|
icon: "🙂",
|
|
display_name: "🙂 Root Article",
|
|
parent_id: false,
|
|
root_article_id: 1,
|
|
parent_path: "1/",
|
|
is_locked: false,
|
|
user_can_write: true,
|
|
user_has_access: true,
|
|
category: "workspace",
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Article 2",
|
|
icon: "🙃",
|
|
display_name: "🙃 Article 2",
|
|
parent_id: 1,
|
|
root_article_id: 1,
|
|
parent_path: "1/2/",
|
|
is_locked: false,
|
|
user_can_write: true,
|
|
user_has_access: true,
|
|
category: "workspace",
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "Article 3",
|
|
icon: false,
|
|
display_name: "Article 3",
|
|
parent_id: 2,
|
|
root_article_id: 1,
|
|
parent_path: "1/2/3/",
|
|
is_locked: false,
|
|
user_can_write: true,
|
|
user_has_access: true,
|
|
category: "workspace",
|
|
},
|
|
{
|
|
id: 4,
|
|
name: "Article 4",
|
|
icon: "😎",
|
|
display_name: "😎 Article 4",
|
|
parent_id: 3,
|
|
root_article_id: 1,
|
|
parent_path: "1/2/3/4/",
|
|
is_locked: false,
|
|
user_can_write: true,
|
|
user_has_access: true,
|
|
category: "workspace",
|
|
},
|
|
{
|
|
id: 5,
|
|
name: "Article 5",
|
|
icon: false,
|
|
display_name: "Article 5",
|
|
parent_id: false,
|
|
root_article_id: 5,
|
|
parent_path: "5/",
|
|
is_locked: false,
|
|
user_can_write: true,
|
|
user_has_access: true,
|
|
category: "workspace",
|
|
},
|
|
];
|
|
}
|
|
|
|
defineMailModels();
|
|
defineModels([Article]);
|
|
|
|
onRpc("get_sidebar_articles", function () {
|
|
return {
|
|
articles: this.env["article"]._filter(),
|
|
favorite_ids: [],
|
|
};
|
|
});
|
|
|
|
onRpc(
|
|
"get_article_hierarchy",
|
|
async function ({ args: [articleId], kwargs: { exclude_article_ids: excludeArticleIds } }) {
|
|
const Article = this.env["article"];
|
|
// returns ancestors of the current article (without the ones given in excludeArticleIds)
|
|
return Article.slice(0, Article.indexOf(Article.browse(articleId)[0]) - 1).filter(
|
|
(article) => !excludeArticleIds.includes(article.id)
|
|
);
|
|
}
|
|
);
|
|
|
|
onRpc("has_access", function () {
|
|
return true;
|
|
});
|
|
|
|
let openArticle;
|
|
|
|
before(() => {
|
|
// Create a component to test the breadcrumb and the hierarchy without the top bar component:
|
|
class KnowledgeNavigation extends Component {
|
|
static template = xml`
|
|
<KnowledgeHierarchy record="props.record"/>
|
|
`;
|
|
static components = {
|
|
KnowledgeHierarchy
|
|
};
|
|
static props = {...standardWidgetProps};
|
|
setup() {
|
|
openArticle = (resId) => this.env.openArticle(resId);
|
|
}
|
|
}
|
|
registry.category("view_widgets").add("knowledge_navigation", {
|
|
component: KnowledgeNavigation,
|
|
fieldDependencies: Object.entries(Article._fields).map(([fieldName, field]) => ({
|
|
name: fieldName,
|
|
type: field.type,
|
|
})),
|
|
});
|
|
});
|
|
after(() => registry.category("view_widgets").remove("knowledge_navigation"));
|
|
|
|
/**
|
|
* Assert that the current article in the hierarchy is the given one
|
|
* @param {number} articleId
|
|
*/
|
|
const assertCurrentArticle = (articleId) => {
|
|
const [article] = MockServer.env["article"].browse(articleId);
|
|
expect(`.o_article_active:contains(${article.name})`).toHaveCount(1);
|
|
};
|
|
|
|
/**
|
|
* Assert that the hierarchy shows the articles in the same order as in the given array.
|
|
* @param {array} articleIds - array of article ids (or false for the dropdown toggle)
|
|
*/
|
|
const assertHierarchyArticles = async (articleIds) => {
|
|
expect(".o_hierarchy_item").toHaveCount(articleIds.length);
|
|
if (articleIds.length > 3) {
|
|
// Open dropdown to check articles between root and parent
|
|
await openHierarchyDropdown();
|
|
}
|
|
MockServer.env["article"].browse(articleIds).forEach((article, idx) => {
|
|
if (idx === articleIds.length - 1) {
|
|
// current article
|
|
expect(`.o_hierarchy_item:last-of-type:contains(${article.icon || "📄"})`).toHaveCount(
|
|
1
|
|
);
|
|
if (article.is_locked || !article.user_can_write) {
|
|
expect(`.o_hierarchy_item:last-of-type:contains(${article.name})`).toHaveCount(1);
|
|
} else {
|
|
expect(`.o_hierarchy_item:last-of-type input:value(${article.name})`).toHaveCount(
|
|
1
|
|
);
|
|
}
|
|
} else if (idx < articleIds.length - 2 && idx > 0) {
|
|
// article in dropdown
|
|
expect(
|
|
`.o-dropdown-item:nth-of-type(${idx}):contains(${article.display_name})`
|
|
).toHaveCount(1);
|
|
} else {
|
|
// root or parent article
|
|
expect(
|
|
`.o_hierarchy_item:nth-of-type(${idx + 1}) a:contains(${article.display_name})`
|
|
).toHaveCount(1);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Click on the given article in the hierarchy
|
|
* @param {number} id of the article to open
|
|
*/
|
|
const clickHierarchyArticle = async (articleId) => {
|
|
const [article] = MockServer.env["article"].browse(articleId);
|
|
const articleName = article.name;
|
|
const articleElement = queryFirst(`.o_hierarchy_item:contains(${articleName})`);
|
|
if (articleElement) {
|
|
await click(articleElement.querySelector("a"));
|
|
} else {
|
|
await openHierarchyDropdown();
|
|
await click(`.o-dropdown-item:contains(${articleName})`);
|
|
}
|
|
return animationFrame();
|
|
};
|
|
|
|
/**
|
|
* open the dropdown in the hierarchy
|
|
*/
|
|
const openHierarchyDropdown = async () => {
|
|
if (queryFirst(".o_hierarchy_item a:contains('...').show")) {
|
|
// dropdown already opened
|
|
return;
|
|
}
|
|
await click(".o_hierarchy_item a:contains('...')");
|
|
return animationFrame();
|
|
};
|
|
|
|
const viewParams = {
|
|
arch: /* xml */ `
|
|
<form js_class="knowledge_article_view_form">
|
|
<widget name="knowledge_navigation" class="d-flex"/>
|
|
</form>
|
|
`,
|
|
resModel: "article",
|
|
type: "form",
|
|
};
|
|
|
|
test("Hierarchy - Open articles in hierarchy", async function () {
|
|
await mountView({ ...viewParams, resId: 4 });
|
|
// Check that root, ellipsis, parent and current article are shown
|
|
await assertHierarchyArticles([1, 2, 3, 4]);
|
|
// Open article 3
|
|
await clickHierarchyArticle(3);
|
|
// Check that root, parent and current article are shown
|
|
await assertHierarchyArticles([1, 2, 3]);
|
|
// Open Article 2
|
|
await clickHierarchyArticle(2);
|
|
// Check that root and current article are shown
|
|
await assertHierarchyArticles([1, 2]);
|
|
// Open Root Article
|
|
await clickHierarchyArticle(1);
|
|
// Check that root is shown
|
|
await assertHierarchyArticles([1]);
|
|
openArticle(4);
|
|
await animationFrame();
|
|
await assertHierarchyArticles([1, 2, 3, 4]);
|
|
// Open Article 2 in the dropdown
|
|
await clickHierarchyArticle(2);
|
|
// Check that article 2 is opened
|
|
assertCurrentArticle(2);
|
|
});
|
|
|
|
test("Hierarchy - Inaccessible articles", async function () {
|
|
Article._records[1].user_has_access = false;
|
|
Article._records[3].user_can_write = false;
|
|
await mountView({ ...viewParams, resId: 4 });
|
|
// Check that the hierarchy is correct and that current article's icon and name are readonly
|
|
await assertHierarchyArticles([1, 2, 3, 4]);
|
|
expect(".o_hierarchy_item a.o_article_emoji").toHaveCount(0);
|
|
expect(".o_hierarchy_item input").toHaveCount(0);
|
|
// Check that the link to the article in the dropdown is disabled
|
|
const [article] = MockServer.env["article"].browse(2);
|
|
expect(`.o-dropdown-item.disabled:contains(${article.display_name})`).toHaveCount(1);
|
|
});
|