Skip to content

Commit fab55e7

Browse files
committed
[IMP] dms: CI artifact pipeline + Hoot suite + OWL 19.0 audit + view-toolbar collapse
Fork-only iteration on top of #3. Five distinct themes folded into one squashed commit since this PR exists for runboat preview / stakeholder review, not upstream review. CI artifact pipeline (test-failure observability) - `Upload screenshots from JS tests` step captures /tmp/odoo_tests/<DB> on failure. Forward-port from the cookiecutter; companion template PR at ledoent/oca-addons-repo-template#1. - `Upload odoo server log on failure` step captures /var/log/odoo/*.log + workspace + runner-temp. - `workflow_dispatch:` so we can manually re-trigger when GitHub silently throttles fork-PR runs after a push burst. - checklog-odoo.cfg ignores `Killing chrome descendants` WARN (benign browser_js cleanup; `OCA_ENABLE_CHECKLOG_ODOO=1` was treating it as a build error). Hoot test suite (tests/test_hoot.py + tests/test_backend_tours.py) - tests/test_hoot.py wires HttpCase.browser_js with `/web/tests?...&filter="@DMS"` — Hoot's `?filter=` defaults to fuzzy character-order matching; the double-quoted exact-substring form isolates @dms/... test paths from the bundled web-core suite. - 9 Hoot test files run + pass under the wiring: dms_stat_bar, file_preview_pane, preview_handlers, preview_registry, file_kanban_buttons / _density / _mount / _list_renderer routing. - `defineMailModels()` covers the mock-server base because dms depends on mail; `expect.errors(N) + verifyErrors(patterns)` replaces the array-of-objects form Hoot rejects. OWL 19.0 readiness audit (per oca-review U/T pattern catalog) - U7 (HIGH): delete dead static/src/js/views/file_kanban_controller.{xml,esm.js}. The XML defined dms.FileKanbanView.Buttons via t-inherit="web.KanbanView.Buttons" — the exact pattern that crashes at OWL render against 19.0's emptied template. Not wired anywhere; the view uses the self-contained dms.KanbanButtons template. - U5 (MEDIUM): coerce boolean t-att-* attributes to explicit 'true'/'false' strings. Owl serializes truthy booleans as a presence flag (<div data-loading>), so CSS selectors `[data-loading="true"]` and tour selectors `[aria-pressed="true"]` silently never matched. Applied to data-loading on the stat bar and to aria-pressed on both density and preview toggles. - U2 (style): convert 10 OWL 1 prototype-assignment idioms (`Foo.template = "x"; Foo.props = {...}`) to OWL 2 static class fields across 7 component classes. Cross-file `Renderer.template = "..."` assignments lifted into the renderer class declarations where they belong. View toolbar collapse (overlap fix) - Both renderers were stacking four absolute-positioned floaters in the top-right corner: preview-toggle pill (kanban + list), density toggle (twice — once in file_kanban.scss, once shifted to right:160px inside the kanban-split context). On list view this put the preview pill on top of the column-header row immediately under Odoo's control panel. - Replaced with `.o_dms_view_toolbar` — one in-flow strip above the grid/list, populated with whatever view-level controls each renderer needs (density btn-group + preview pill on kanban; preview pill only on list, right-anchored via `__spacer { flex: 1 }`). Gone: 4 absolute-position rules, 2 `right: calc(40% + 12px)` shifts, 1 `display: none` density hack, all z-index battles. Backend e2e tour - tests/test_backend_tours.py::test_kanban_density_toggle drives start_tour("/odoo", "dms_kanban_density_tour", login="admin"). Five steps verify default density → click Compact → localStorage persistence + data-attr propagation → restore Comfortable → cleanup. Toolbar chrome renders even with zero records, so the tour is safe under OCA CI's --without-demo=all. Signed-off-by: Don Kendall <dkendall@ledoweb.com>
1 parent 7fec59a commit fab55e7

29 files changed

Lines changed: 388 additions & 271 deletions

.github/workflows/test.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,23 @@ jobs:
6969
run: oca_init_test_database
7070
- name: Run tests
7171
run: oca_run_tests
72+
- name: Upload screenshots from JS tests
73+
uses: actions/upload-artifact@v4
74+
if: ${{ failure() }}
75+
with:
76+
name: Screenshots of failed JS tests - ${{ matrix.name }}
77+
path: /tmp/odoo_tests/${{ env.PGDATABASE }}
78+
if-no-files-found: ignore
79+
- name: Upload odoo server log on failure
80+
uses: actions/upload-artifact@v4
81+
if: ${{ failure() }}
82+
with:
83+
name: Odoo server log - ${{ matrix.name }}
84+
path: |
85+
/var/log/odoo/*.log
86+
${{ github.workspace }}/odoo.log
87+
${{ runner.temp }}/odoo*.log
88+
if-no-files-found: ignore
7289
- uses: codecov/codecov-action@v4
7390
with:
7491
token: ${{ secrets.CODECOV_TOKEN }}

checklog-odoo.cfg

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
[checklog-odoo]
22
ignore=
33
WARNING.* 0 failed, 0 error\(s\).*
4+
# browser_js cleanup logs a warning when killing lingering chrome
5+
# children — benign by design (cleanup IS doing its job), but fails
6+
# CHECKLOG. Only matches the exact cleanup phrase to keep the filter
7+
# narrow.
8+
WARNING.*Killing chrome descendants-or-self.*

dms/static/src/js/components/dms_stat_bar.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
-->
66
<templates xml:space="preserve">
77
<t t-name="dms.StatBar">
8-
<div class="o_dms_stat_bar" t-att-data-loading="isLoading">
8+
<div class="o_dms_stat_bar" t-att-data-loading="isLoading ? 'true' : 'false'">
99
<div
1010
class="o_dms_stat_bar__tile"
1111
t-foreach="props.tiles"

dms/static/src/js/fields/path_json/path_owl.esm.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import {standardFieldProps} from "@web/views/fields/standard_field_props";
99
import {useService} from "@web/core/utils/hooks";
1010

1111
class DmsPathField extends Component {
12+
static template = "dms.DmsPathField";
13+
static props = {
14+
...standardFieldProps,
15+
};
16+
1217
setup() {
1318
super.setup();
1419
this.action = useService("action");
@@ -35,11 +40,6 @@ class DmsPathField extends Component {
3540
}
3641
}
3742

38-
DmsPathField.template = "dms.DmsPathField";
39-
DmsPathField.props = {
40-
...standardFieldProps,
41-
};
42-
4343
const dmsPathField = {
4444
component: DmsPathField,
4545
displayName: _t("Dms Path Field"),

dms/static/src/js/fields/preview_binary/preview_record.esm.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import {useFileViewer} from "@web/core/file_viewer/file_viewer_hook";
1010
import {useService} from "@web/core/utils/hooks";
1111

1212
export class PreviewRecordField extends BinaryField {
13+
static template = "dms.FilePreviewField";
14+
static props = {
15+
...standardFieldProps,
16+
};
17+
1318
setup() {
1419
super.setup();
1520
this.store = useService("mail.store");
@@ -29,11 +34,6 @@ export class PreviewRecordField extends BinaryField {
2934
}
3035
}
3136

32-
PreviewRecordField.template = "dms.FilePreviewField";
33-
PreviewRecordField.props = {
34-
...standardFieldProps,
35-
};
36-
3737
const previewRecordField = {
3838
component: PreviewRecordField,
3939
displayName: _t("Preview Record"),

dms/static/src/js/views/dms_directory_kanban_renderer.esm.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import {onWillStart, useState} from "@odoo/owl";
77
import {useService} from "@web/core/utils/hooks";
88

99
export class DmsDirectoryKanbanRenderer extends KanbanRenderer {
10+
static template = "dms.DirectoryKanbanRenderer";
11+
static components = {
12+
...KanbanRenderer.components,
13+
DmsStatBar,
14+
};
15+
1016
setup() {
1117
super.setup();
1218
this.orm = useService("orm");
@@ -24,9 +30,3 @@ export class DmsDirectoryKanbanRenderer extends KanbanRenderer {
2430
return this.statsState.stats;
2531
}
2632
}
27-
28-
DmsDirectoryKanbanRenderer.components = {
29-
...KanbanRenderer.components,
30-
DmsStatBar,
31-
};
32-
DmsDirectoryKanbanRenderer.template = "dms.DirectoryKanbanRenderer";

dms/static/src/js/views/file_kanban_controller.esm.js

Lines changed: 0 additions & 12 deletions
This file was deleted.

dms/static/src/js/views/file_kanban_controller.xml

Lines changed: 0 additions & 25 deletions
This file was deleted.

dms/static/src/js/views/file_kanban_renderer.esm.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ function _readStoredPreview() {
4242
}
4343

4444
export class FileKanbanRenderer extends KanbanRenderer {
45+
static template = "dms.KanbanRenderer";
46+
static components = {
47+
...KanbanRenderer.components,
48+
KanbanRecord: FileKanbanRecord,
49+
FilePreviewPane,
50+
};
51+
4552
setup() {
4653
super.setup();
4754
this.densityState = useState({density: _readStoredDensity()});
@@ -128,9 +135,3 @@ export class FileKanbanRenderer extends KanbanRenderer {
128135
}
129136
}
130137
}
131-
132-
FileKanbanRenderer.components = {
133-
...KanbanRenderer.components,
134-
KanbanRecord: FileKanbanRecord,
135-
FilePreviewPane,
136-
};

dms/static/src/js/views/file_kanban_renderer.xml

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,6 @@
2020
Release to add to the current directory
2121
</div>
2222
</div>
23-
<div
24-
class="o_kanban_dms_density_toggle btn-group btn-group-sm"
25-
role="group"
26-
aria-label="Card density"
27-
>
28-
<button
29-
type="button"
30-
t-foreach="densityOptions"
31-
t-as="opt"
32-
t-key="opt.value"
33-
t-att-class="{
34-
'btn': true,
35-
'btn-secondary': density === opt.value,
36-
'btn-outline-secondary': density !== opt.value,
37-
}"
38-
t-att-title="opt.label"
39-
t-att-aria-pressed="density === opt.value"
40-
t-on-click="() => this.setDensity(opt.value)"
41-
>
42-
<i t-attf-class="fa #{opt.icon}" />
43-
</button>
44-
</div>
4523
</xpath>
4624
<xpath expr="//div[hasclass('o_kanban_renderer')]" position="attributes">
4725
<attribute name="t-att-data-density">density</attribute>
@@ -54,30 +32,61 @@
5432
t-att-data-preview-open="previewOpen ? 'true' : 'false'"
5533
>
5634
<div class="o_dms_kanban_split__main">
35+
<div
36+
class="o_dms_view_toolbar"
37+
role="toolbar"
38+
aria-label="View options"
39+
>
40+
<div
41+
class="o_kanban_dms_density_toggle btn-group btn-group-sm"
42+
role="group"
43+
aria-label="Card density"
44+
>
45+
<button
46+
type="button"
47+
t-foreach="densityOptions"
48+
t-as="opt"
49+
t-key="opt.value"
50+
t-att-class="{
51+
'btn': true,
52+
'btn-secondary': density === opt.value,
53+
'btn-outline-secondary': density !== opt.value,
54+
}"
55+
t-att-title="opt.label"
56+
t-att-aria-pressed="density === opt.value ? 'true' : 'false'"
57+
t-on-click="() => this.setDensity(opt.value)"
58+
>
59+
<i t-attf-class="fa #{opt.icon}" />
60+
</button>
61+
</div>
62+
<span class="o_dms_view_toolbar__spacer" />
63+
<button
64+
type="button"
65+
class="o_dms_view_toolbar__toggle btn btn-sm d-flex align-items-center gap-1"
66+
t-att-class="{
67+
'o_dms_view_toolbar__toggle--on': previewOpen,
68+
'o_dms_view_toolbar__toggle--off': !previewOpen,
69+
}"
70+
t-att-title="previewOpen ? 'Hide preview pane (Esc)' : 'Show side-by-side preview'"
71+
t-att-aria-pressed="previewOpen ? 'true' : 'false'"
72+
t-on-click="() => this.togglePreview()"
73+
>
74+
<i
75+
t-attf-class="fa #{previewOpen ? 'fa-times' : 'fa-columns'}"
76+
/>
77+
<span class="d-none d-md-inline">
78+
<t t-if="previewOpen">Hide preview</t>
79+
<t t-else="">Preview pane</t>
80+
</span>
81+
</button>
82+
</div>
5783
<t t-call="dms.KanbanRendererBase" />
5884
</div>
5985
<FilePreviewPane
6086
t-if="previewOpen"
6187
recordId="previewRecordId"
6288
onClose="() => closePreview()"
6389
/>
64-
<button
65-
type="button"
66-
class="o_dms_kanban_split__toggle btn btn-sm d-flex align-items-center gap-1"
67-
t-att-class="{
68-
'o_dms_kanban_split__toggle--on': previewOpen,
69-
'o_dms_kanban_split__toggle--off': !previewOpen,
70-
}"
71-
t-att-title="previewOpen ? 'Hide preview pane (Esc)' : 'Show side-by-side preview'"
72-
t-att-aria-pressed="previewOpen"
73-
t-on-click="() => this.togglePreview()"
74-
>
75-
<i t-attf-class="fa #{previewOpen ? 'fa-times' : 'fa-columns'}" />
76-
<span class="d-none d-md-inline">
77-
<t t-if="previewOpen">Hide preview</t>
78-
<t t-else="">Preview pane</t>
79-
</span>
80-
</button>
8190
</div>
8291
</t>
8392

0 commit comments

Comments
 (0)