# AIlab-comparison-engine
Comparison Engine refactored from AIlab-UI-Diff using Object-Oriented Programming.
#### Setup
- install dependencies using `poetry lock` and `poetry install`
- create `.env` file with `API_KEY`
- run the virtual environment using `poetry shell`
- run the Flask server with `python app.py`
- go to `frontend` folder and install dependencies using `npm install`
- run the Node server with `node server.js`
- go to http://localhost:3000/compare
- select test case to compare
- run the comparison engine
- wait a few seconds
- select elements to display  
    
On the next run, you can launch the comparison engine using `./run_servers.sh`  
Give it execute permissions using `chmod +x run_servers.sh`    
---
#### Endpoints
Backend (Flask)   
- `/` [GET]: **docs**   
- `/api/compare` [POST]: **json payload (diffs without DOM trees)**   
- `/api/compare/preprocess` [POST]: **preprocess DOM trees**   
- `/api/compare/detailed` [POST]: **detailed DOM trees**   
- `/api/compare/layout` [POST]: **layout DOM trees**   
    
Frontend (Node)   
- `/compare` [GET]: **selection tool**
- `/compare` [POST]: **rendered results**
    
The Flask server is launched on port `8001`   
The Node server is launched on port `3000`   
---
#### Request
The request must be in JSON format (keys provided below).  
In case of a missing URL for the DOM files, the algorithm will be changed to `pixel`.  
You can pass data to the engine in 3 ways.  
1. Provide the comparison ID and environment:   
```json
{
    "comparisonId": "z9WOyd9V",
    "env": "prod"
}
```
2. Send test case containing required keys:  
(`test_case_name` and `comparison_mode` are optional)
**The options for ``"algorithm"`` are ``"PIXEL"``, ``"DOM-ELEMENT"``, and ``"AI-MODEL"``.**
**Comparison using ``"AI-MODEL"`` will do pixel comparison as preprocessing.** So if the results from ``"AI-MODEL"`` look wierd, try ``"PIXEL"`` to see the diffs before model detection.
**For branch UIDIFF-114, ``"DOM-ELEMENT"`` is not recommended. Bugs may exist.**
```json
{   
    "test_case_name": "Example Page SB",
    "options": {
        "algorithm": "dom-element",
        "moved_tolerance": "30",
        "comparison_mode": "detailed"
    },
    "base_dom": "https://ailab.dev.visualtest.io/test-case-01-example-page-sb/base.json",
    "target_dom": "https://ailab.dev.visualtest.io/test-case-01-example-page-sb/target.json",
    "base_img": "https://ailab.dev.visualtest.io/test-case-01-example-page-sb/base.png",
    "target_img": "https://ailab.dev.visualtest.io/test-case-01-example-page-sb/target.png"
}
```
3. Send a file (directly or via URL) containing a test case list and choose one in `/compare`.
---
#### Response
The response is in JSON format (keys provided below).   
By default, there is no `elements` dictionary.  
DOM elements are provided on separate endpoints.   
```json
{
    "diffs": {
        "detailed": "detailed_diffs",
        "layout": "layout_diffs",
    },
    "images": {
        "base": {
            "url": "base_img_file.img_url",
            "height": "base_img_file.height",
            "width": "base_img_file.width",
        },
        "target": {
            "url": "target_img_file.img_url",
            "height": "target_img_file.height",
            "width": "target_img_file.width",
        }
    },
    "elements": {
        "preprocess": {
            "base": "base_dom.preprocessed_dom_list",
            "target": "target_dom.preprocessed_dom_list",
        },
        "detailed": {
            "base": "base_dom.detailed_tree converted to list",
            "target": "target_dom.detailed_tree converted to list",
        },
        "layout": {
            "base": "base_dom.layout_tree converted to list",
            "target": "target_dom.layout_tree converted to list",
        }
    },
    "logs": {
        "errors":"logs with level equal to error",
        "warnings": "logs with level equal to warning",
        "info": "logs with level equal to info",
        "debug": "logs with level equal to debug",
    },
    "config": "the whole configuration",
    "version": "number",
}
```
---
#### Diffs
All `elements` are converted into `detailed_diffs`.  
This is necessary to display elements in the `comparison-view`.  
`detailed_diffs` and `layout_diffs` share the same format as the following example:
```
[
    {
        "diff_type": "changed", 
        "base": {
            "id": 0, 
            "tag": "IMG", 
            "top": 770, 
            "left": 592, 
            "width": 1072, 
            "height": 628, 
            "dom_id": 308, 
            "xpath": "/HTML[1]/BODY[1]/DIV[3]", 
            "css_selector": " > HTML#1 > BODY#39 > DIV#73.pt_saspage inline-flow"
        }, 
        "target": {
            "id": 0, 
            "tag": "IMG", 
            "top": 772, 
            "left": 592, 
            "width": 1072, 
            "height": 674, 
            "dom_id": 304, 
            "xpath": "/HTML[1]/BODY[1]/DIV[3]", 
            "css_selector": " > HTML#1 > BODY#36 > DIV#70.pt_saspage inline-flow"
        }, 
        "similarity_score": 0.689, 
        "sensitivity": "null for detailed diff and value range in [0, 1, 2] for layout diff", , 
        "description": [
            "Element content changed", 
            "Element height grew 46px"
        ], 
        "ignore": false
    },
    {
        "diff_type": "added", 
        "base": {}, 
        "target": {
            "id": 31, 
            "tag": "DIV", 
            "top": 2736, 
            "left": 548, 
            "width": 2360, 
            "height": 754, 
            "dom_id": 558, 
            "xpath": "/HTML[1]/BODY[1]/DIV[3]/MAIN[1]", 
            "css_selector": " > HTML#1 > BODY#36 > DIV#70.pt_saspage inline-flow > MAIN#228.clearfix"
            }, 
        "similarity_score": null, 
        "sensitivity": "null for detailed diff and value range in [0, 1, 2] for layout diff", 
        "description": [
            "Element was added"
            ], 
        "ignore": false
    }
]
```