From 40a5ba4b9e5410b0477e169be1d25bd6df7cb1f7 Mon Sep 17 00:00:00 2001 From: Arthur van de Vondervoort Date: Sun, 13 Sep 2026 16:23:43 +0200 Subject: [PATCH 1/5] docs: add TA0002 rule page for actions invoked on a directly opened part test page Co-Authored-By: Claude Fable 5.1 --- .../analyzers/TestAutomationCop/TA0002.md | 109 ++++++++++++++++++ .../analyzers/TestAutomationCop/_index.md | 3 +- 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 content/docs/analyzers/TestAutomationCop/TA0002.md diff --git a/content/docs/analyzers/TestAutomationCop/TA0002.md b/content/docs/analyzers/TestAutomationCop/TA0002.md new file mode 100644 index 0000000..2432be2 --- /dev/null +++ b/content/docs/analyzers/TestAutomationCop/TA0002.md @@ -0,0 +1,109 @@ ++++ +title = 'Actions cannot be invoked on a part page opened directly through its own TestPage variable' +linkTitle = 'TA0002' + +[params] + id = 'TA0002' + severity = 'Warning' + category = 'Usage' + codeAction = false + ignoreObsolete = true ++++ + +A `ListPart` or `CardPart` page opened directly through its own `TestPage` variable renders no actions at runtime. The platform resolves actions only when the part is hosted inside another page and reached through its `part(...)` control. Calling `Invoke()`, `Enabled()`, or `Visible()` on any action of a directly opened part page fails with: + +> The action with ID = xxx is not found on the page. + +To reach the action in a test, open the hosting page and invoke the action through the part control: `MainPage.SubPagePart.MyAction.Invoke()`. + +### Example + +{{< highlight al "hl_lines=14" >}} +page 50100 "Item Subpage" +{ + PageType = ListPart; + SourceTable = Item; + + actions + { + area(processing) + { + action(UpdatePrices) { } + } + } +} + +codeunit 50100 "Item Test" +{ + Subtype = Test; + + [Test] + procedure PriceUpdateTest() + var + ItemSubPage: TestPage "Item Subpage"; + begin + ItemSubPage.OpenView(); + ItemSubPage.UpdatePrices.Invoke(); // Action 'UpdatePrices' cannot be invoked on page 'Item Subpage' opened directly because its PageType is ListPart; invoke it through the part control of the hosting page instead. [TA0002] + end; +} +{{< /highlight >}} + +Open the hosting page instead and invoke the action through its part control. + +{{< highlight al "hl_lines=13" >}} +page 50101 "Item Card" +{ + PageType = Card; + SourceTable = Item; + + layout + { + area(content) + { + part(ItemLines; "Item Subpage") { } + } + } +} + +codeunit 50100 "Item Test" +{ + Subtype = Test; + + [Test] + procedure PriceUpdateTest() + var + ItemCard: TestPage "Item Card"; + begin + ItemCard.OpenView(); + ItemCard.ItemLines.UpdatePrices.Invoke(); + end; +} +{{< /highlight >}} + +### When the diagnostic is reported + +- The receiver is a `TestPage` variable (local, global, or `var` parameter) whose target page has `PageType = ListPart` or `PageType = CardPart`. +- The call is `Invoke()`, `Enabled()`, or `Visible()` on any action of that page — these are the only members of the built-in `TestAction` class, and all three fail identically at runtime. +- Actions added by a `pageextension` that extends the part page are included. +- An obsolete part page (`ObsoleteState = Pending`) is still reported — the runtime failure is real regardless of obsolete state. + +### Exception + +The diagnostic is not raised for: + +- **Field access** on a directly opened part page — fields work without the hosting page. +- **`OpenView()`, `OpenEdit()`, `OpenNew()`** and other `TestPage` built-in methods — these are not `TestAction` members. +- **Built-in system actions** `OK()`, `Cancel()`, `Yes()`, `No()`, `View()`, `Edit()` — these return a `TestAction` through a method call, not through an `ITestActionAccess`, so the analyzer does not flag them. +- **`TestRequestPage`** variables — request pages are not `ListPart` or `CardPart`. +- **Obsolete test code** — when the enclosing method or object is marked obsolete, the diagnostic is skipped. + +```al +#pragma warning disable TA0002 // The part page is opened directly to verify field rendering only; no actions are invoked in this path. + ItemSubPage.UpdatePrices.Invoke(); +#pragma warning restore TA0002 +``` + +### See also + +- [New rule to warn about SubPage.SomeAction.Invoke()](https://github.com/ALCops/Analyzers/discussions/455) on GitHub Discussions +- [TestPage Data Type](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/testpage/testpage-data-type) on Microsoft Learn diff --git a/content/docs/analyzers/TestAutomationCop/_index.md b/content/docs/analyzers/TestAutomationCop/_index.md index 7db9103..5f6f0be 100644 --- a/content/docs/analyzers/TestAutomationCop/_index.md +++ b/content/docs/analyzers/TestAutomationCop/_index.md @@ -4,10 +4,11 @@ type: docs no_list: true --- -TestAutomationCop inspects test codeunits and is silent on production code. It flags test procedures whose structure or attributes keep the test runner from executing them as intended, such as a global procedure in a test codeunit that is not marked as a test method. +TestAutomationCop inspects test codeunits and is silent on production code. It flags test code whose structure keeps the test runner from executing it as intended, such as a missing `[Test]` attribute or an action invoked on a part page that renders no actions at runtime. ## Rules | ID | Title | Severity | Enabled | Code Fix | |---|---|---|---|---| | [TA0001](ta0001/) | Global procedures in test codeunits must be test methods | Warning | ✓ | | +| [TA0002](ta0002/) | Actions cannot be invoked on a part page opened directly through its own TestPage variable | Warning | ✓ | | From 88c20c6fb192e00dc8f4b4249922d0793ea719be Mon Sep 17 00:00:00 2001 From: Arthur van de Vondervoort Date: Sun, 13 Sep 2026 16:26:34 +0200 Subject: [PATCH 2/5] docs: fix highlighted lines and the suppression example on the TA0002 page Co-Authored-By: Claude Fable 5.1 --- content/docs/analyzers/TestAutomationCop/TA0002.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/docs/analyzers/TestAutomationCop/TA0002.md b/content/docs/analyzers/TestAutomationCop/TA0002.md index 2432be2..e9f1e57 100644 --- a/content/docs/analyzers/TestAutomationCop/TA0002.md +++ b/content/docs/analyzers/TestAutomationCop/TA0002.md @@ -18,7 +18,7 @@ To reach the action in a test, open the hosting page and invoke the action throu ### Example -{{< highlight al "hl_lines=14" >}} +{{< highlight al "hl_lines=25" >}} page 50100 "Item Subpage" { PageType = ListPart; @@ -50,7 +50,7 @@ codeunit 50100 "Item Test" Open the hosting page instead and invoke the action through its part control. -{{< highlight al "hl_lines=13" >}} +{{< highlight al "hl_lines=24" >}} page 50101 "Item Card" { PageType = Card; @@ -93,13 +93,13 @@ The diagnostic is not raised for: - **Field access** on a directly opened part page — fields work without the hosting page. - **`OpenView()`, `OpenEdit()`, `OpenNew()`** and other `TestPage` built-in methods — these are not `TestAction` members. -- **Built-in system actions** `OK()`, `Cancel()`, `Yes()`, `No()`, `View()`, `Edit()` — these return a `TestAction` through a method call, not through an `ITestActionAccess`, so the analyzer does not flag them. +- **Built-in system actions** `OK()`, `Cancel()`, `Yes()`, `No()`, `View()`, `Edit()` — these are methods of the `TestPage` itself, not actions declared on the part page. - **`TestRequestPage`** variables — request pages are not `ListPart` or `CardPart`. - **Obsolete test code** — when the enclosing method or object is marked obsolete, the diagnostic is skipped. ```al -#pragma warning disable TA0002 // The part page is opened directly to verify field rendering only; no actions are invoked in this path. - ItemSubPage.UpdatePrices.Invoke(); +#pragma warning disable TA0002 // This test deliberately asserts the platform error for an action on a directly opened part. + asserterror ItemSubPage.UpdatePrices.Invoke(); #pragma warning restore TA0002 ``` From 437c59513d5afa63ff1d35b6b64189dcae0ef1a2 Mon Sep 17 00:00:00 2001 From: Arthur van de Vondervoort Date: Sun, 13 Sep 2026 16:27:51 +0200 Subject: [PATCH 3/5] docs: highlight the corrected statement in the TA0002 fixed example Co-Authored-By: Claude Fable 5.1 --- content/docs/analyzers/TestAutomationCop/TA0002.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/analyzers/TestAutomationCop/TA0002.md b/content/docs/analyzers/TestAutomationCop/TA0002.md index e9f1e57..92e18bf 100644 --- a/content/docs/analyzers/TestAutomationCop/TA0002.md +++ b/content/docs/analyzers/TestAutomationCop/TA0002.md @@ -50,7 +50,7 @@ codeunit 50100 "Item Test" Open the hosting page instead and invoke the action through its part control. -{{< highlight al "hl_lines=24" >}} +{{< highlight al "hl_lines=25" >}} page 50101 "Item Card" { PageType = Card; From 626e83e559740bf69f76a3fab11c7ad5eb2f46e1 Mon Sep 17 00:00:00 2001 From: Arthur van de Vondervoort Date: Sun, 13 Sep 2026 16:46:57 +0200 Subject: [PATCH 4/5] docs: TA0002 also reports obsolete test code Co-Authored-By: Claude Fable 5.1 --- content/docs/analyzers/TestAutomationCop/TA0002.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/content/docs/analyzers/TestAutomationCop/TA0002.md b/content/docs/analyzers/TestAutomationCop/TA0002.md index 92e18bf..274d5bb 100644 --- a/content/docs/analyzers/TestAutomationCop/TA0002.md +++ b/content/docs/analyzers/TestAutomationCop/TA0002.md @@ -7,7 +7,7 @@ linkTitle = 'TA0002' severity = 'Warning' category = 'Usage' codeAction = false - ignoreObsolete = true + ignoreObsolete = false +++ A `ListPart` or `CardPart` page opened directly through its own `TestPage` variable renders no actions at runtime. The platform resolves actions only when the part is hosted inside another page and reached through its `part(...)` control. Calling `Invoke()`, `Enabled()`, or `Visible()` on any action of a directly opened part page fails with: @@ -85,7 +85,7 @@ codeunit 50100 "Item Test" - The receiver is a `TestPage` variable (local, global, or `var` parameter) whose target page has `PageType = ListPart` or `PageType = CardPart`. - The call is `Invoke()`, `Enabled()`, or `Visible()` on any action of that page — these are the only members of the built-in `TestAction` class, and all three fail identically at runtime. - Actions added by a `pageextension` that extends the part page are included. -- An obsolete part page (`ObsoleteState = Pending`) is still reported — the runtime failure is real regardless of obsolete state. +- Obsolete code is not exempt: an obsolete part page, an `[Obsolete]` test method and an `ObsoleteState = Pending` test codeunit are all reported, because the test runner still executes the test and the runtime failure is real. ### Exception @@ -95,7 +95,6 @@ The diagnostic is not raised for: - **`OpenView()`, `OpenEdit()`, `OpenNew()`** and other `TestPage` built-in methods — these are not `TestAction` members. - **Built-in system actions** `OK()`, `Cancel()`, `Yes()`, `No()`, `View()`, `Edit()` — these are methods of the `TestPage` itself, not actions declared on the part page. - **`TestRequestPage`** variables — request pages are not `ListPart` or `CardPart`. -- **Obsolete test code** — when the enclosing method or object is marked obsolete, the diagnostic is skipped. ```al #pragma warning disable TA0002 // This test deliberately asserts the platform error for an action on a directly opened part. From a32130ece6953e7ecf4ea907ae6fc13d805d3033 Mon Sep 17 00:00:00 2001 From: Arthur van de Vondervoort Date: Sun, 13 Sep 2026 17:13:36 +0200 Subject: [PATCH 5/5] docs: drop the discussion link from the TA0002 See also section Co-Authored-By: Claude Fable 5.1 --- content/docs/analyzers/TestAutomationCop/TA0002.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/docs/analyzers/TestAutomationCop/TA0002.md b/content/docs/analyzers/TestAutomationCop/TA0002.md index 274d5bb..331119b 100644 --- a/content/docs/analyzers/TestAutomationCop/TA0002.md +++ b/content/docs/analyzers/TestAutomationCop/TA0002.md @@ -104,5 +104,4 @@ The diagnostic is not raised for: ### See also -- [New rule to warn about SubPage.SomeAction.Invoke()](https://github.com/ALCops/Analyzers/discussions/455) on GitHub Discussions - [TestPage Data Type](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/testpage/testpage-data-type) on Microsoft Learn