Files
goodie/frontend/sale/static/tests/sale_order_line_field.test.js
Marc 8741282322 Eliminate Python dependency: embed frontend assets in odoo-go
- Copy all OWL frontend assets (JS/CSS/XML/fonts/images) into frontend/
  directory (2925 files, 43MB) — no more runtime reads from Python Odoo
- Replace OdooAddonsPath config with FrontendDir pointing to local frontend/
- Rewire bundle.go, static.go, templates.go, webclient.go to read from
  frontend/ instead of external Python Odoo addons directory
- Auto-detect frontend/ and build/ dirs relative to binary in main.go
- Delete obsolete Python helper scripts (tools/*.py)

The Go server is now fully self-contained: single binary + frontend/ folder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 23:09:12 +02:00

252 lines
9.7 KiB
JavaScript

import { defineMailModels } from '@mail/../tests/mail_test_helpers';
import { expect, test } from '@odoo/hoot';
import { queryAllTexts } from '@odoo/hoot-dom';
import {
clickCancel,
contains,
defineModels,
fields,
mountView,
} from '@web/../tests/web_test_helpers';
import { defineComboModels } from '@product/../tests/product_combo_test_helpers';
import { saleModels } from './sale_test_helpers';
class SaleOrderLine extends saleModels.SaleOrderLine {
_records = [
{ id: 1, name: "Non Combo Line1", product_id: 1, sequence: 1 },
{ id: 2, name: "Non Combo Line2", product_id: 2, sequence: 2 },
{ id: 3, name: "Test Combo1", product_id: 5, sequence: 3, product_type: 'combo' },
{ id: 4, name: "Combo1 Item 1", product_id: 3, combo_item_id: 3, linked_line_id: 3, sequence: 4 },
{ id: 5, name: "Combo1 Item 2", product_id: 1, combo_item_id: 1, linked_line_id: 3, sequence: 5 },
{ id: 6, name: "Test Combo2", product_id: 5, sequence: 6, product_type: 'combo' },
{ id: 7, name: "Combo2 Item 1", product_id: 4, combo_item_id: 4, linked_line_id: 6, sequence: 7 },
{ id: 8, name: "Combo2 Item 2", product_id: 2, combo_item_id: 2, linked_line_id: 6, sequence: 8 },
{ id: 9, name: "Non Combo Line3", product_id: 1, sequence: 9 },
];
}
class SaleOrder extends saleModels.SaleOrder {
_records = [
{
id: 1,
name: "Combo Sale order",
order_line: SaleOrderLine._records.map(record => record.id),
},
]
}
defineComboModels();
defineModels({ SaleOrderLine, SaleOrder });
defineMailModels();
test("test combo move up/down", async () => {
await mountView({
type: 'form',
resModel: 'sale.order',
resId: 1,
arch: `
<form>
<field
name="order_line"
widget="sol_o2m"
options="{'subsections': True}"
>
<list editable="bottom">
<control>
<create name="add_line_control" string="Add a line"/>
<create name="add_section_control" string="Add a section" context="{'default_display_type': 'line_section'}"/>
<create name="add_note_control" string="Add a note" context="{'default_display_type': 'line_note'}"/>
</control>
<field name="sequence" widget="handle" invisible="combo_item_id"/>
<field name="name"/>
<field name="display_type" column_invisible="1"/>
<field name="linked_line_id" column_invisible="1"/>
<field name="product_type" column_invisible="1"/>
<field name="combo_item_id" column_invisible="1"/>
</list>
</field>
</form>
`,
});
expect(queryAllTexts('.o_data_row')).toEqual([
"Non Combo Line1",
"Non Combo Line2",
"Test Combo1",
"Combo1 Item 1",
"Combo1 Item 2",
"Test Combo2",
"Combo2 Item 1",
"Combo2 Item 2",
"Non Combo Line3",
]);
await contains('.o_data_row:contains(Test Combo1) .o_list_section_options button').click();
expect('.o-dropdown-item:contains(Move Up)').toHaveCount(1, {
message: 'Move up option should be there for Test combo1 line having SO lines before and after'
});
expect('.o-dropdown-item:contains(Move Down)').toHaveCount(1, {
message: 'Move down option should be there for Test combo1 line having SO lines before and after'
});
await contains('.o-dropdown-item:contains(Move Up)').click();
await contains('.o_data_row:contains(Test Combo1) .o_list_section_options button').click();
await contains('.o-dropdown-item:contains(Move Up)').click();
await contains('.o_data_row:contains(Test Combo1) .o_list_section_options button').click();
expect('.o-dropdown-item:contains(Move Up)').toHaveCount(0, {
message: 'Move up option should be invisible for Test combo1 since there aren\'t any non combo SO lines before'
});
await contains('.o_data_row:contains(Test Combo2) .o_list_section_options button').click();
expect('.o-dropdown-item:contains(Move Up)').toHaveCount(1, {
message: 'Move up option should be there for Test combo2 line having SO lines before and after'
});
expect('.o-dropdown-item:contains(Move Down)').toHaveCount(1, {
message: 'Move down option should be there for Test combo2 line having SO lines before and after'
});
await contains('.o-dropdown-item:contains(Move Down)').click();
await contains('.o_data_row:contains(Test Combo2) .o_list_section_options button').click();
expect('.o-dropdown-item:contains(Move Down)').toHaveCount(0, {
message: 'Move down option should be invisible for Test combo2 since there aren\'t any non combo SO lines after'
});
expect(queryAllTexts('.o_data_row')).toEqual([
"Test Combo1",
"Combo1 Item 1",
"Combo1 Item 2",
"Non Combo Line1",
"Non Combo Line2",
"Non Combo Line3",
"Test Combo2",
"Combo2 Item 1",
"Combo2 Item 2",
], {
message: 'Test combo1 line should be moved up two lines and Test combo2 line should be moved down one line'
});
await clickCancel();
expect(queryAllTexts('.o_data_row')).toEqual([
"Non Combo Line1",
"Non Combo Line2",
"Test Combo1",
"Combo1 Item 1",
"Combo1 Item 2",
"Test Combo2",
"Combo2 Item 1",
"Combo2 Item 2",
"Non Combo Line3",
]);
await contains('.o_data_row:contains(Test Combo1) .o_list_section_options button').click();
await contains('.o-dropdown-item:contains(Move Down)').click();
expect(queryAllTexts('.o_data_row')).toEqual([
"Non Combo Line1",
"Non Combo Line2",
"Test Combo2",
"Combo2 Item 1",
"Combo2 Item 2",
"Test Combo1",
"Combo1 Item 1",
"Combo1 Item 2",
"Non Combo Line3",
], {
message: 'Test combo1 and Test combo2 should be swapped when moving Test combo1 down'
});
await clickCancel();
await contains('.o_data_row:contains(Test Combo2) .o_list_section_options button').click();
await contains('.o-dropdown-item:contains(Move Up)').click();
expect(queryAllTexts('.o_data_row')).toEqual([
"Non Combo Line1",
"Non Combo Line2",
"Test Combo2",
"Combo2 Item 1",
"Combo2 Item 2",
"Test Combo1",
"Combo1 Item 1",
"Combo1 Item 2",
"Non Combo Line3",
], {
message: 'Test combo1 and Test combo2 should be swapped when moving Test combo2 up'
});
})
test("Test combo columns", async () => {
// Set different defaults for checking aggregation of columns on combo line
SaleOrderLine._fields.price_unit = fields.Float({ default: 3.00 });
SaleOrderLine._fields.price_total = fields.Float({ default: 3.00 });
SaleOrderLine._fields.product_uom_qty = fields.Float({ default: 3.00 });
SaleOrderLine._fields.discount = fields.Integer({ default: 30 });
await mountView({
type: 'form',
resModel: 'sale.order',
resId: 1,
arch: `
<form>
<field
name="order_line"
widget="sol_o2m"
options="{'subsections': True}"
aggregated_fields="price_total"
>
<list editable="bottom">
<control>
<create name="add_line_control" string="Add a line"/>
<create name="add_section_control" string="Add a section" context="{'default_display_type': 'line_section'}"/>
<create name="add_note_control" string="Add a note" context="{'default_display_type': 'line_note'}"/>
</control>
<field name="sequence" widget="handle" invisible="combo_item_id"/>
<field name="name"/>
<field name="price_unit"/>
<field name="product_uom_qty"/>
<field name="discount"/>
<field name="price_total"/>
<field name="display_type" column_invisible="1"/>
<field name="linked_line_id" column_invisible="1"/>
<field name="product_type" column_invisible="1"/>
<field name="combo_item_id" column_invisible="1"/>
</list>
</field>
</form>
`,
});
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual([
"Non Combo Line1",
"Non Combo Line2",
"Test Combo1",
"Combo1 Item 1",
"Combo1 Item 2",
"Test Combo2",
"Combo2 Item 1",
"Combo2 Item 2",
"Non Combo Line3",
]);
expect(queryAllTexts('.o_data_row:contains(Test Combo1) > td').filter(Boolean)).toEqual([
"Test Combo1", // name
"3.00", // product_uom_qty
"30", // discount
"9.00", // price_total
], {
message: 'combo line should only have name, product_uom_qty, discount and `aggregated_fields` columns'
});
expect(queryAllTexts('.o_data_row:contains(Non Combo Line1) > td').filter(Boolean)).toEqual([
"Non Combo Line1", // name
"3.00", // price_unit
"3.00", // product_uom_qty
"30", // discount
"3.00", // price_total
], {
message: 'Non-combo line should have all columns'
});
})