How to quickly and efficiently replace imported UI

Hi all.

I would like to talk about my experience in import substitution of UI.

I’m not proposing anything new, just a well-known approach applied to a specific situation.

The task can be briefly described as follows:

There is: An initial project with many tables and forms.

Task: It is necessary to create similar tables and forms in the target project using a different technology. In fact, there are many classes and files of the same type, differing in previously known parts.

Solution:

1. Automatically collect the necessary information from the source project into a file.

2. Add to this file the part that could not be collected automatically.

3. Create the necessary files using a template, replacing wildcard characters in the template with values ​​from the file. The resulting files can be modified if necessary.

The first thing that comes to mind when solving such a problem is to use the code generator built into VisualStudio; it implements a similar task. I initially considered using it. But I changed my mind. It turns out to be much more complicated. In addition, the generator in the studio is supposed to be used constantly; my generator is needed one-time to create a code template. Although I do not rule out that I will continue to use it.

Now more details.

For several years I participated in the development of a Web application on the net framework 4.7 whose UI layer was built on Devexpress. At one point, a command was received to import the application. Actually, import substitution can be divided into 3 parts.

1. Changing the database (a separate task, not about it now)

2. Changing framework 4.7 to .NET (performed quite simply using upgrade-assistant)

3. Change of UI (you need to move away from Devexpress as an enemy product).

It was decided to make the UI in the new application using MVC in order to reduce the differences between the new and old code. At least the controllers and cshtml files location remains the same. This made my task much easier. As a new UI layer, it was decided to use pure Vue + library for displaying tables. To simplify things, we abandoned the idea of ​​using SPA. Each page must be loaded separately. Also, for the sake of simplification, it was decided to abandon assemblers and load js libraries via cdn. (I’ll be glad to hear about the pros and cons of this solution in the comments.)

A significant part of our initial Web application is tabular forms and relatively simple cards for them. There are many of them, more than 70, written at different times and by different people, and naturally in different ways, what is exactly the same in them is the use of Devexpress tables. To avoid the same mess in the new application, I was tasked with developing a sample code that would be used in the new application. This sample was agreed upon by all developers. While working on the sample, the idea to create code automatically was born. The code automatically created for the tabular part can be left unchanged in 90% of cases; changes most often relate to adding field buttons to an additional filter. The editing form is more difficult. I'm creating a template with fields that are found in the model. The type of fields and their relative position on the form need to be finalized.

The stand (prototype) on which I tested this solution was published on GitHub SergiyShest/DevExpressToDevExtremeMigrate (github.com). Just in case, I will say that all this code was written in my personal time on my own initiative and is not the property of anyone other than me. The templates used to create a new application are also different. (For example, I had to use Vue 2 in a way that the company may use browsers that do not support the JavaScript module).

The prototype has 3 applications:

1) Source: Files of the source application on net framework 4.7 + MVC and Devexpress. The application is not built because I deleted everything unnecessary and left only the necessary files. I once found this application as an example for learning Devexpress.

2) Target: Target application in .NET MVC. Originally created using a template in VisualStudio. A Core project has been added to it, in which the same Entity files are placed in the Entity folder as in the original application. In order not to bother with the database, the TestDataHelper class is also located there, which returns a randomly filled IQueryable simulating working with the database. Basic Generic controllers and necessary js files have been added to the UI project

3) CodeGenerator: A generator that, based on Devexpress tables in the source project, creates similar tables in the target project, but using a different technology (Devextreme). Templates are also in this project.

To speed up development and provide flexibility, the generator is controlled by running tests. This is useful for programs that are executed only by the developer.

Tests for management are separated from the tests that I used during development and placed in a separate GeneratorCommand class:

1) CollectInfo(): Initial collection of information into a file

2) GenerateAll(): Generate everything.

3) GeneratePart(): Generate code for a single table.

There are also flags that allow you to skip the creation of any of the files.

Suggested algorithm:

1) Place the desired source application in place of the Source project and/or correct the paths in the CodeGeneration.cs and InfoCollector.cs files.

2) Place the target application in place of the Target project and/or correct the paths in the CodeGeneration.cs and InfoCollector.cs files.

3) The target application must contain all the Entities from the source application for which forms are supposed to be created.

4) Collect data by running the GeneratorCommand.CollectInfo() test, the logic of which is approximately as follows:

a) In the source UI project, all files using the template _*grid*.cshtml are searched. These files contain a description of the table columns and usually there is a model by which you can understand which Entity class is associated with a view or table in the database. (sometimes this information has to be pulled out of the controller).

b) I’m looking for an Entity class to take field descriptions for the edit form (to make the search faster, you must set the EntityPath property in the InfoCollector class)

c) I save some of the collected information into a file (collector.json).

5) Add or edit the collected information.

a) Since due to the heterogeneity of the source code, it is not possible to obtain the correct model names, etc. for all values, the file must be supplemented manually.

b) Objects in the json file for which there is no need for auto-generation must be deleted (commented out) or the AlwaysSkip flag must be set.

c) The edited file should be placed in CodeGenerator\templates\collector.json (I specifically create it in a different place to exclude the possibility of overwriting it when running tests

6) Get the necessary code by running the GeneratorCommand.GenerateAll() command, the logic of which is approximately as follows:

a) The file collector.json is read.

b) Additional information is collected for each object in the file (composition of columns and form fields)

c) The code of the required classes is created using a template. For each object the following classes:

i) Table Form Controller

ii) Tabular view

iii) Form Controller

iv) View forms

v) Js test

vi) For ease of debugging, a menu is also generated.

d) The files are saved in the same location and with the same name as in the original application (thanks for choosing MVC)

e) If necessary, it is possible to disable the generation of any of the classes in the list above.

7) Debugging the application. Everything is standard here.

8) And finally, my favorite topic, the icing on the cake, so to speak, TESTS.

For testing, I used end to end tests written in Cypress (what I like about Cypress is that it gives stable results).

In the demo example, the simplest tests for a tabular form are a kind of Smoke Tests.

· Checking for header presence

· Checking that if the filter values ​​are empty, there is data in the table

· Checking that by setting cross dates in the filter you get an empty table

The test logic uses the feature that most tables in the template have an additional filter by dates. This allows you to set the filter values ​​in such a way that you get an empty table. For those tables for which there is no date, the test text needs to be corrected during testing (in a good way, of course, you should automatically create a slightly different test for such cases, but you haven’t gotten around to it).

Tests can be run from Visual Studio via Test Explorer. It looks like this.

Running tests in VisualStudio

Running tests in VisualStudio

While debugging, I ran tests directly through Cypress. At some stage this is necessary, but I don't like the need to change the context (tool) while working. In addition, running tests from Visual Studio through the test mechanism theoretically makes it possible to carry out some manipulations with the database, or what is much more beautiful is to launch the application with a special config so that the wet adapter for working with the database is loaded.

To make it possible to use NUnit to run js tests, I had to write a small adapter. I may have invented the bicycle here, but the bicycle is simple and works reliably.

In a real application, tests are also written on cards, and we also had to take into account the need for authorization. For authorization, I chose the simplest possible approach, that is, before each test, I open the authorization form and enter the data of the test user. Cypress copes with this task easily.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *