I would like to simulate the creation of something like a customer, item or invoice, although through the use of test pages. My first thought was to use the OpenNew()
-function:
[Test]
[HandlerFunctions('ModalPageHandler,ExpectedConfirmHandler')]
procedure SampleTest()
// [FEATURE] Customer
// [SCENARIO] Create a new Customer
// [PREREQ] super rights not necessary
var
tpCustomerCard: TestPage "Customer Card";
cNewCustomerNo: Code[20];
rCustomer: Record Customer;
begin
Initialize();
// [GIVEN] Office 365 Business Full permissions
gAny.SetSeed(5);
gLibraryLowerPermissions.SetO365BusFull();
// [WHEN] Creating a new user
tpCustomerCard.OpenNew();
tpCustomerCard.Name.SetValue(gAny.AlphabeticText(gAny.IntegerInRange(20)));
cNewCustomerNo := tpCustomerCard."No.".Value;
gLibraryVariableStorage.Enqueue('');
gLibraryVariableStorage.Enqueue(true);
tpCustomerCard.Close();
// [THEN] create user
gAssert.IsFalse(cNewCustomerNo = '', 'New Customer No. created');
gAssert.IsTrue(rCustomer.Get(cNewCustomerNo), 'New Customer created');
end;
[ModalPageHandler]
procedure ModalPageHandler(var tpCustomerCard: TestPage "Customer Card");
begin
end;
[ConfirmHandler]
procedure ExpectedConfirmHandler(pQuestion: Text[1024]; VAR pvReply: Boolean)
// Call the following in the Test function
//gLibraryVariableStorage.Enqueue('ExpectedConfirmText');
//gLibraryVariableStorage.Enqueue(true); // or false, depending of the reply you want if below question is asked. Any other question will throw an error
begin
gAssert.ExpectedMessage(gLibraryVariableStorage.DequeueText(), pQuestion);
pvReply := gLibraryVariableStorage.DequeueBoolean();
end;
However, this would always return the following error:
Unexpected CLR exception thrown.: Microsoft.Dynamics.Framework.UI.FormAbortException: Page New - Customer Card has to close. ---> Microsoft.Dynamics.Nav.Types.Exceptions.NavNCLMissingUIHandlerException: Unhandled UI: ModalPage 1340 ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> Microsoft.Dynamics.Nav.Types.Exceptions.NavNCLMissingUIHandlerException: Unhandled UI: ModalPage 1340\ at Microsoft.Dynamics.Nav.Runtime.NavTestExecution.FindHandler(NavHandlerType handlerType, NavApplicationObjectBase appObject, Boolean throwIfNotFound, String handlerDescription)\ at Microsoft.Dynamics.Nav.Runtime.NavTestExecution.TestHandleModalForm(NavForm form, FormResult& action, NavFormRuntimeParameters parameters)\ at Microsoft.Dynamics.Nav.Runtime.NavForm.RunModal(NavRecord record, Int32 fieldNo)\ at Microsoft.Dynamics.Nav.Runtime.NavFormHandle.RunModal(NavRecord record, Int32 fieldNo)\ at Microsoft.Dynamics.Nav.BusinessApplication.Record1300.NewCustomerFromTemplate_Scope__1848449490.OnRun()\ at Microsoft.Dynamics.Nav.Runtime.NavMethodScope.Run()\ at Microsoft.Dynamics.Nav.BusinessApplication.Record1300.NewCustomerFromTemplate(INavRecordHandle customer)\ at Microsoft.Dynamics.Nav.BusinessApplication.Record1300.OnInvoke(Int32 memberId, Object[] args)\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit1389.CreateCustomerFromConfigTemplate_Scope_587295807.OnRun()\ at Microsoft.Dynamics.Nav.Runtime.NavMethodScope.Run()\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit1389.CreateCustomerFromConfigTemplate(INavRecordHandle customer, ByRef`1 isHandled)\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit1389.OnInsertCustomerFromTemplateHandler_Scope.OnRun()\ at Microsoft.Dynamics.Nav.Runtime.NavMethodScope.Run()\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit1389.OnInsertCustomerFromTemplateHandler(INavRecordHandle customer, ByRef`1 result, ByRef`1 isHandled)\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit1389.OnInvoke(Int32 memberId, Object[] args)\ at Microsoft.Dynamics.Nav.EventSubscription.NavEventScope.CallEventSubscriberInternal(NavEventSubscription subscriber, NavApplicationObjectBase subscriberInstance, Object[] parameters)\ at Microsoft.Dynamics.Nav.EventSubscription.NavEventScope.CallEventSubscriber(NavApplicationObjectBase callingApplicationObject, NavEventSubscription subscriber, NavApplicationObjectBase subscriberInstance, Object[] parameters)\ at Microsoft.Dynamics.Nav.EventSubscription.NavEventScope.ProcessCallToTypeAndManualSubscriptions(NavApplicationObjectBase callerApplicationObject, NavEventSubscription[] subscriptions, PrepareParametersCallBack prepareParameters)\ at Microsoft.Dynamics.Nav.Runtime.NavMethodScope.RunEvent()\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit1381.OnInsertCustomerFromTemplate(INavRecordHandle customer, ByRef`1 result, ByRef`1 isHandled)\ at Microsoft.Dynamics.Nav.Runtime.NavMethodScope.Run()\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit1381.InsertCustomerFromTemplate(INavRecordHandle customer)\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit1381.OnInvoke(Int32 memberId, Object[] args)\ at Microsoft.Dynamics.Nav.BusinessApplication.Page21.CreateCustomerFromTemplate_Scope_857168676.OnRun()\ at Microsoft.Dynamics.Nav.Runtime.NavMethodScope.Run()\ at Microsoft.Dynamics.Nav.BusinessApplication.Page21.CreateCustomerFromTemplate()\ at Microsoft.Dynamics.Nav.BusinessApplication.Page21.OnAfterGetCurrRecord_Scope.OnRun()\ at Microsoft.Dynamics.Nav.Runtime.NavMethodScope.Run()\ at Microsoft.Dynamics.Nav.BusinessApplication.Page21.OnAfterGetCurrRecord()\ at Microsoft.Dynamics.Nav.Runtime.NavForm.RaiseOnAfterGetCurrRecord()\ at Microsoft.Dynamics.Nav.Runtime.NavForm.AfterGetCurrRecord()\ --- End of inner exception stack trace ---\ at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)\ at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)\ at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)\ at Microsoft.Dynamics.Nav.Runtime.NavApplicationMethod.InvokeMethod(ITreeObject obj, String methodName, Object[] args, Boolean resolveHandler, Boolean throwOnNotFound)\ --- End of inner exception stack trace ---\ at Microsoft.Dynamics.Nav.Runtime.NavApplicationMethod.InvokeMethod(ITreeObject obj, String methodName, Object[] args, Boolean resolveHandler, Boolean throwOnNotFound)\ at Microsoft.Dynamics.Nav.Service.NSFormApplicationCode.<>c__DisplayClass7_0.<Invoke>b__0(NsDataAccess dataAccess)\ at Microsoft.Dynamics.Nav.Service.NsFormDataAccess.RunWithFormDataAccess[T](ITreeObject parent, NavForm form, Func`2 func)\ at Microsoft.Dynamics.Nav.Service.NsDataAccess.RunWithDataAccess[T](ITreeObject parent, NavRecordState state, Boolean instantiateNewForm, Guid parentFormHandle, String parentControlName, Boolean& instantiatedForm, Func`2 func)\ at Microsoft.Dynamics.Nav.Service.NsDataAccess.RunWithDataAccess[T](ITreeObject parent, NavRecordState state, Func`2 func)\ at INavServiceWrapper.InvokeApplicationMethod(ApplicationMethodRequest , NavRecordState )\ at Microsoft.Dynamics.Nav.Types.AsyncNavServiceWrapper.<>c__DisplayClass119_0.<BeginInvokeApplicationMethod>b__0()\ at Microsoft.Dynamics.Nav.Types.AsyncNavServiceWrapper.Invoke(Func`1 action, AsyncCallback callback, Object asyncState, Object extraState)\ at Microsoft.Dynamics.Nav.Client.ServiceConnectionBase.<>c__DisplayClass21_0.<InvokeApplicationMethod>b__0(IAsyncNavService server)\ at Microsoft.Dynamics.Nav.Runtime.TestServiceConnection.CallServer[T](BeginCallServerMethod beginCallServerMethod, EndCallServerMethod`1 endCallServerMethod)\ at Microsoft.Dynamics.Nav.Client.ServiceConnectionBase.InvokeApplicationMethod(ApplicationCodeType objectType, Int32 objectId, String methodName, NavDataSet& dataSet, NavRecordState& state, Object[]& args)\ at Microsoft.Dynamics.Nav.Client.DataBinder.NstDataAccess.InvokeOnAfterGetCurrentRecord(NavDataSet& navDataSet, NavRecordState& navRecordState)\ at Microsoft.Dynamics.Nav.Client.DataBinder.NstDataAccess.InvokeOnNewRecord(NavRecord record, NavRecord xrec, NavRecordState state, Object[] args)\ at Microsoft.Dynamics.Nav.Client.DataBinder.NstDataAccess.OnNewRecord(Boolean clearFormState, NavRecord xrec, Object[] args)\ --- End of inner exception stack trace ---\ at Microsoft.Dynamics.Nav.Client.DataBinder.NstDataAccess.Abort(NavBaseException exception)\ at Microsoft.Dynamics.Nav.Client.DataBinder.NstDataAccess.OnNewRecord(Boolean clearFormState, NavRecord xrec, Object[] args)\ at Microsoft.Dynamics.Nav.Client.DataBinder.NavBindingManager.EnsureOnNewRecordIsUpToDate()\ at Microsoft.Dynamics.Nav.Client.DataBinder.NavBindingManager.OnCurrentRowChanged(CurrentRowChangeEventArgs e)\ at Microsoft.Dynamics.Framework.UI.BindingManager.set_CurrentCollectionIndex(Int32 value)\ at Microsoft.Dynamics.Framework.UI.VirtualRows.UpdateAfterInsert(Int32 index, Boolean needsUpdateCurrentRow)\ at Microsoft.Dynamics.Framework.UI.VirtualRows.AddWithoutValidation(RowEntry row, Boolean needsUpdateCall, Boolean needsUpdateCurrentRow, Boolean insertFirst, Boolean raiseEvents)\ at Microsoft.Dynamics.Framework.UI.VirtualRows.Insert()\ at Microsoft.Dynamics.Nav.Client.TestPageClient.TestPageClientSession.CreateLogicalForm(Int32 formId, FormState formState)\ at Microsoft.Dynamics.Nav.Client.TestPageClient.TestPageClientSession.CreatePage(Int32 id, ViewMode mode)\ at Microsoft.Dynamics.Nav.Runtime.NavTestPage.Open(ViewMode mode)\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit50100.SampleTest_Scope__1526614456.OnRun()\ at Microsoft.Dynamics.Nav.Runtime.NavMethodScope.Run()\ at Microsoft.Dynamics.Nav.BusinessApplication.Codeunit50100.SampleTest()
When debugging, the code automatically breaks at this function in Mini Customer Template.dal
, even without breakpoints:
[Obsolete('Will be removed with other functionality related to "old" templates. Replaced by new templates.', '18.0')]
procedure NewCustomerFromTemplate(var Customer: Record Customer): Boolean
var
ConfigTemplateHeader: Record "Config. Template Header";
ConfigTemplates: Page "Config Templates";
begin
ConfigTemplateHeader.SetRange("Table ID", DATABASE::Customer);
ConfigTemplateHeader.SetRange(Enabled, true);
if ConfigTemplateHeader.Count = 1 then begin
ConfigTemplateHeader.FindFirst;
InsertCustomerFromTemplate(ConfigTemplateHeader, Customer);
exit(true);
end;
if (ConfigTemplateHeader.Count > 1) and GuiAllowed then begin
ConfigTemplates.SetTableView(ConfigTemplateHeader);
ConfigTemplates.LookupMode(true);
ConfigTemplates.SetNewMode;
if ConfigTemplates.RunModal = ACTION::LookupOK then begin
ConfigTemplates.GetRecord(ConfigTemplateHeader);
InsertCustomerFromTemplate(ConfigTemplateHeader, Customer);
exit(true);
end;
end;
exit(false);
end;
To be specific, it always stops at the last if
, most likely because ConfigTemplates
is undefined. This probably is because when normally creating a new customer or item, the page Config Templates
is opened first to let the user pick a template, and since my codeunit is skipping this step, one of the variables ends up being undefined. I then tried to open the Customer Card
page by invoking the New Customer
action from the Customer List
page, but since that is not a custom action, I can't reach it.
To replicate user interaction as closely as possible I'd prefer to open windows through actions found in the UI, but if that's impossible with AL, I'd at least want the former method to work. How can I open a card page using templates without the test immediately failing?
If you look into the error message you will see that it complains about a missing ModalPageHandler
for ModalPage 1340
which is the Config Templates
page.
You need to define a ModalPageHandler
for that specific page:
[ModalPageHandler]
procedure ConfigTemplatesHandler(var ConfigTemplates: TestPage "Config Templates")
begin
// This will select the first template available
ConfigTemplates.OK().Invoke();
end;
Then you need to add it as a HandlerFunction
for your test procedure:
[Test]
[HandlerFunctions('ConfigTemplatesHandler,ExpectedConfirmHandler')]
procedure SampleTest()
begin
...
end;