430 lines
13 KiB
JavaScript
430 lines
13 KiB
JavaScript
import { click, visit } from "@ember/test-helpers";
|
|
import { test } from "qunit";
|
|
import { cloneJSON } from "discourse/lib/object";
|
|
import topicFixtures from "discourse/tests/fixtures/topic";
|
|
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
|
|
|
const TOPIC_ID = 28830;
|
|
|
|
const accountsReport = {
|
|
report_type: "accounts",
|
|
period: { begin: null, end: null },
|
|
sections: [
|
|
{
|
|
title: null,
|
|
rows: [
|
|
{
|
|
account: "assets",
|
|
label: "Assets",
|
|
depth: 0,
|
|
root: true,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "1000.0", display: "1000.00 EUR" },
|
|
],
|
|
},
|
|
{
|
|
account: "assets:bank",
|
|
label: "Bank",
|
|
depth: 1,
|
|
root: false,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "1000.0", display: "1000.00 EUR" },
|
|
],
|
|
},
|
|
{
|
|
account: "assets:bank:checking",
|
|
label: "Checking",
|
|
depth: 2,
|
|
root: false,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "1000.0", display: "1000.00 EUR" },
|
|
],
|
|
},
|
|
],
|
|
total: [{ commodity: "EUR", quantity: "1000.0", display: "1000.00 EUR" }],
|
|
},
|
|
],
|
|
net: null,
|
|
meta: { hledger_version: "1.52.1" },
|
|
};
|
|
|
|
const equityReport = {
|
|
report_type: "equity",
|
|
period: { begin: null, end: null },
|
|
groups: [
|
|
{
|
|
commodity: "EUR",
|
|
total: { commodity: "EUR", quantity: "1000.0", display: "1000.0 EUR" },
|
|
rows: [
|
|
{
|
|
account: "equity:alice",
|
|
label: "Alice",
|
|
depth: 0,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "600.0", display: "600.0 EUR" },
|
|
],
|
|
percentage: "60.00",
|
|
},
|
|
{
|
|
account: "equity:bob",
|
|
label: "Bob",
|
|
depth: 0,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "400.0", display: "400.0 EUR" },
|
|
],
|
|
percentage: "40.00",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
meta: { hledger_version: "1.52.1" },
|
|
};
|
|
|
|
const balanceSheetReport = {
|
|
report_type: "balance_sheet",
|
|
period: { begin: null, end: null },
|
|
sections: [
|
|
{
|
|
title: "Assets",
|
|
rows: [
|
|
{
|
|
account: "assets:bank",
|
|
label: "Bank",
|
|
depth: 0,
|
|
root: false,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "1000.0", display: "1000.00 EUR" },
|
|
],
|
|
},
|
|
{
|
|
account: "assets:bank:checking",
|
|
label: "Checking",
|
|
depth: 1,
|
|
root: false,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "1000.0", display: "1000.00 EUR" },
|
|
],
|
|
},
|
|
],
|
|
total: [{ commodity: "EUR", quantity: "1000.0", display: "1000.00 EUR" }],
|
|
},
|
|
{
|
|
title: "Liabilities",
|
|
rows: [],
|
|
total: [{ commodity: "EUR", quantity: "0.0", display: "0.00 EUR" }],
|
|
},
|
|
],
|
|
net: [{ commodity: "EUR", quantity: "1000.0", display: "1000.00 EUR" }],
|
|
meta: { hledger_version: "1.52.1" },
|
|
};
|
|
|
|
const incomeStatementReport = {
|
|
report_type: "income_statement",
|
|
period: { begin: null, end: null },
|
|
sections: [
|
|
{
|
|
title: "Revenues",
|
|
rows: [
|
|
{
|
|
account: "revenues:donations",
|
|
label: "Donations",
|
|
depth: 0,
|
|
root: false,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "50.0", display: "50.00 EUR" },
|
|
],
|
|
},
|
|
],
|
|
total: [{ commodity: "EUR", quantity: "50.0", display: "50.00 EUR" }],
|
|
},
|
|
{
|
|
title: "Expenses",
|
|
rows: [
|
|
{
|
|
account: "expenses:hosting",
|
|
label: "Hosting",
|
|
depth: 0,
|
|
root: false,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "20.5", display: "20.50 EUR" },
|
|
],
|
|
},
|
|
{
|
|
account: "expenses:hosting:server",
|
|
label: "Server",
|
|
depth: 1,
|
|
root: false,
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "20.5", display: "20.50 EUR" },
|
|
],
|
|
},
|
|
],
|
|
total: [{ commodity: "EUR", quantity: "20.5", display: "20.50 EUR" }],
|
|
},
|
|
],
|
|
net: [{ commodity: "EUR", quantity: "29.5", display: "29.50 EUR" }],
|
|
meta: { hledger_version: "1.52.1" },
|
|
};
|
|
|
|
const transactionsReport = {
|
|
report_type: "transactions",
|
|
period: { begin: null, end: null },
|
|
transactions: [
|
|
{
|
|
date: "2024-01-01",
|
|
date2: null,
|
|
description: "Opening balances",
|
|
code: "",
|
|
postings: [
|
|
{
|
|
account: "assets:bank:checking",
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "1000.0", display: "1000.00 EUR" },
|
|
],
|
|
},
|
|
{
|
|
account: "equity:alice",
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "-600.0", display: "-600.00 EUR" },
|
|
],
|
|
},
|
|
{
|
|
account: "equity:bob",
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "-400.0", display: "-400.00 EUR" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
date: "2024-02-10",
|
|
date2: null,
|
|
description: "Hosting",
|
|
code: "",
|
|
postings: [
|
|
{
|
|
account: "expenses:hosting:server",
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "20.5", display: "20.50 EUR" },
|
|
],
|
|
},
|
|
{
|
|
account: "assets:bank:checking",
|
|
amounts: [
|
|
{ commodity: "EUR", quantity: "-20.5", display: "-20.50 EUR" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
meta: { hledger_version: "1.52.1" },
|
|
};
|
|
|
|
acceptance("Hledger plugin", function (needs) {
|
|
needs.settings({ hledger_enabled: true });
|
|
|
|
needs.pretender((server, helper) => {
|
|
server.get("/t/45.json", () => {
|
|
const topic = cloneJSON(topicFixtures["/t/28830/1.json"]);
|
|
topic.post_stream.posts[0].cooked = `<div class="hledger-dashboard"></div>`;
|
|
return helper.response(topic);
|
|
});
|
|
|
|
server.get(`/hledger/topics/${TOPIC_ID}/reports/accounts`, () =>
|
|
helper.response(accountsReport)
|
|
);
|
|
server.get(`/hledger/topics/${TOPIC_ID}/reports/equity`, () =>
|
|
helper.response(equityReport)
|
|
);
|
|
server.get(`/hledger/topics/${TOPIC_ID}/reports/balance_sheet`, () =>
|
|
helper.response(balanceSheetReport)
|
|
);
|
|
server.get(`/hledger/topics/${TOPIC_ID}/reports/income_statement`, () =>
|
|
helper.response(incomeStatementReport)
|
|
);
|
|
server.get(`/hledger/topics/${TOPIC_ID}/reports/transactions`, () =>
|
|
helper.response(transactionsReport)
|
|
);
|
|
});
|
|
|
|
test("renders the dashboard in place of the journal", async function (assert) {
|
|
await visit("/t/-/45");
|
|
|
|
assert.dom(".hledger-dashboard__toolbar .btn").exists({ count: 5 });
|
|
assert
|
|
.dom(".hledger-dashboard__toolbar .btn:nth-of-type(1)")
|
|
.hasText("Accounts");
|
|
assert
|
|
.dom(".hledger-dashboard__toolbar .btn:nth-of-type(2)")
|
|
.hasText("Balance sheet");
|
|
assert
|
|
.dom(".hledger-dashboard__toolbar .btn:nth-of-type(3)")
|
|
.hasText("P&L");
|
|
assert
|
|
.dom(".hledger-dashboard__toolbar .btn:nth-of-type(4)")
|
|
.hasText("Equity");
|
|
assert
|
|
.dom(".hledger-dashboard__toolbar .btn:nth-of-type(5)")
|
|
.hasText("Journal");
|
|
assert
|
|
.dom(
|
|
".hledger-dashboard__toolbar .btn:nth-of-type(2) .d-icon-scale-balanced"
|
|
)
|
|
.exists("renders the balance sheet icon");
|
|
assert
|
|
.dom(".hledger-dashboard__toolbar .btn:nth-of-type(3) .d-icon-chart-line")
|
|
.exists("renders the income statement icon");
|
|
assert.dom(".hledger-dashboard__table").includesText("Total");
|
|
assert.dom(".hledger-dashboard__table").includesText("Assets");
|
|
assert.dom(".hledger-dashboard__table").includesText("Bank");
|
|
assert.dom(".hledger-dashboard__table").includesText("Checking");
|
|
assert.dom(".hledger-dashboard__table").includesText("1000.00 EUR");
|
|
assert.dom(".hledger-dashboard__account.--depth-1").hasText("Bank");
|
|
assert.dom(".hledger-dashboard__account.--depth-2").hasText("Checking");
|
|
assert
|
|
.dom('.hledger-dashboard__account[title="assets:bank"]')
|
|
.hasText("Bank");
|
|
|
|
const rootAccount = document.querySelector(
|
|
".hledger-dashboard__account.--depth-0"
|
|
);
|
|
const childAccount = document.querySelector(
|
|
".hledger-dashboard__account.--depth-1"
|
|
);
|
|
const grandchildAccount = document.querySelector(
|
|
".hledger-dashboard__account.--depth-2"
|
|
);
|
|
const amountCell = rootAccount
|
|
.closest("tr")
|
|
.querySelector(".hledger-dashboard__amount");
|
|
const paddingStart = (element) =>
|
|
parseFloat(
|
|
getComputedStyle(element).getPropertyValue("padding-inline-start")
|
|
);
|
|
|
|
assert.true(
|
|
paddingStart(childAccount) > paddingStart(rootAccount),
|
|
"indents child accounts"
|
|
);
|
|
assert.true(
|
|
paddingStart(grandchildAccount) > paddingStart(childAccount),
|
|
"indents grandchild accounts"
|
|
);
|
|
assert.true(
|
|
["end", "right"].includes(getComputedStyle(amountCell).textAlign),
|
|
"aligns amounts to the end"
|
|
);
|
|
assert.true(
|
|
parseInt(
|
|
getComputedStyle(
|
|
document.querySelector(".hledger-dashboard__account.--root")
|
|
).fontWeight,
|
|
10
|
|
) >= 700,
|
|
"bolds root accounts"
|
|
);
|
|
});
|
|
|
|
test("switches to the equity distribution", async function (assert) {
|
|
await visit("/t/-/45");
|
|
|
|
await click(".hledger-dashboard__toolbar .btn:nth-of-type(4)");
|
|
|
|
assert.dom(".hledger-dashboard__table").includesText("Alice");
|
|
assert.dom(".hledger-dashboard__table").includesText("60.00%");
|
|
assert
|
|
.dom('.hledger-dashboard__account[title="equity:alice"]')
|
|
.hasText("Alice");
|
|
assert.dom(".hledger-dashboard__account.--depth-0").exists({ count: 2 });
|
|
assert
|
|
.dom(".hledger-dashboard__account.--root")
|
|
.doesNotExist("equity rows are never bold");
|
|
});
|
|
|
|
test("renders the balance sheet and income statement as trees", async function (assert) {
|
|
await visit("/t/-/45");
|
|
|
|
await click(".hledger-dashboard__toolbar .btn:nth-of-type(2)");
|
|
|
|
assert.dom(".hledger-dashboard__section-title").exists({ count: 2 });
|
|
assert
|
|
.dom('.hledger-dashboard__account[title="assets:bank"]')
|
|
.hasText("Bank");
|
|
assert
|
|
.dom('.hledger-dashboard__account[title="assets:bank:checking"]')
|
|
.hasText("Checking");
|
|
assert
|
|
.dom(".hledger-dashboard__account.--depth-0")
|
|
.hasText("Bank", "starts the tree flush under the heading");
|
|
assert.dom(".hledger-dashboard__account.--depth-1").hasText("Checking");
|
|
assert
|
|
.dom(".hledger-dashboard__account.--root")
|
|
.doesNotExist("no root rows once the heading stands in for the root");
|
|
|
|
const bank = document.querySelector(
|
|
'.hledger-dashboard__account[title="assets:bank"]'
|
|
);
|
|
const checking = document.querySelector(
|
|
'.hledger-dashboard__account[title="assets:bank:checking"]'
|
|
);
|
|
|
|
assert.true(
|
|
parseFloat(
|
|
getComputedStyle(checking).getPropertyValue("padding-inline-start")
|
|
) >
|
|
parseFloat(
|
|
getComputedStyle(bank).getPropertyValue("padding-inline-start")
|
|
),
|
|
"indents below the heading's first level"
|
|
);
|
|
|
|
assert
|
|
.dom(".hledger-dashboard__empty")
|
|
.exists({ count: 1 }, "shows a placeholder for the empty section");
|
|
assert.dom(".hledger-dashboard__empty").hasText("No data");
|
|
assert
|
|
.dom(
|
|
".hledger-dashboard__section:nth-of-type(2) .hledger-dashboard__table"
|
|
)
|
|
.doesNotExist("omits the table for the empty section");
|
|
assert
|
|
.dom(
|
|
".hledger-dashboard__section:nth-of-type(1) .hledger-dashboard__table"
|
|
)
|
|
.exists("keeps the table for the populated section");
|
|
|
|
await click(".hledger-dashboard__toolbar .btn:nth-of-type(3)");
|
|
|
|
assert
|
|
.dom('.hledger-dashboard__account[title="revenues:donations"]')
|
|
.hasText("Donations");
|
|
assert
|
|
.dom('.hledger-dashboard__account[title="expenses:hosting:server"]')
|
|
.hasText("Server");
|
|
});
|
|
|
|
test("shows the transaction log", async function (assert) {
|
|
await visit("/t/-/45");
|
|
|
|
await click(".hledger-dashboard__toolbar .btn:nth-of-type(5)");
|
|
|
|
assert
|
|
.dom(".hledger-dashboard__transaction")
|
|
.exists({ count: 2 }, "renders one block per transaction");
|
|
assert
|
|
.dom(
|
|
".hledger-dashboard__transaction:nth-of-type(1) .hledger-dashboard__transaction-date"
|
|
)
|
|
.hasText("2024-01-01", "shows the transaction date");
|
|
assert
|
|
.dom(
|
|
".hledger-dashboard__transaction:nth-of-type(1) .hledger-dashboard__transaction-description"
|
|
)
|
|
.hasText("Opening balances", "shows the transaction description");
|
|
assert
|
|
.dom(".hledger-dashboard__table")
|
|
.includesText("assets:bank:checking");
|
|
assert.dom(".hledger-dashboard__table").includesText("1000.00 EUR");
|
|
});
|
|
});
|