#!/usr/bin/python
# encoding=utf-8

"""
@Author  :  Lijiawei
@Date    :  11/4/2021 10:14 AM
@Desc    :  Scaffold web Generator.
"""

import os
import platform
import sys

from loguru import logger


class ExtraArgument:
    create_venv = False


def init_parser_scaffold_web(subparsers):
    sub_parser_scaffold = subparsers.add_parser(
        "startproject-web", help="Create a new Web project with template structure."
    )
    sub_parser_scaffold.add_argument(
        "project_name", type=str, nargs="?", help="Specify new project name."
    )
    sub_parser_scaffold.add_argument(
        "-venv",
        dest="create_venv",
        action="store_true",
        help="Create virtual environment in the project, and install utx.",
    )
    return sub_parser_scaffold


def create_scaffold_web(project_name):
    """ Create scaffold with specified project name.
    """
    if os.path.isdir(project_name):
        logger.warning(
            f"Project folder {project_name} exists, please specify a new project name."
        )
        return 1
    elif os.path.isfile(project_name):
        logger.warning(
            f"Project name {project_name} conflicts with existed file, please specify a new one."
        )
        return 1

    logger.info(f"Create new project: {project_name}")
    print(f"Project root dir: {os.path.join(os.getcwd(), project_name)}\n")

    def create_folder(path):
        os.makedirs(path)
        msg = f"Created folder: {path}"
        print(msg)

    def create_file(path, file_content=""):
        with open(path, "w", encoding="utf-8") as f:
            f.write(file_content)
        msg = f"Created file: {path}"
        print(msg)

    create_folder(project_name)
    create_folder(os.path.join(project_name, "config"))
    create_folder(os.path.join(project_name, "logs"))
    create_folder(os.path.join(project_name, "driver"))
    create_folder(os.path.join(project_name, "report/airtest"))
    create_folder(os.path.join(project_name, "tests"))
    create_folder(os.path.join(project_name, "suites"))

    content = """venv
.idea/
.pytest_cache
*/*__pycache__/
report
debug
*.pyc
"""
    create_file(os.path.join(project_name, ".gitignore"), content)

    content = """# UTX


## 安装
- 命令行执行
```
pip install -U utx
```


## 设计理念

很大程度上借鉴了HttpRunner（优秀的框架）。不同的是，utx更着重写python，而不是写YAML文件。

- 简单是更好的
- 每个人都能用python写自动化

这就是utx的设计理念。


## 项目结构
![](https://files.mdnice.com/user/17535/555b68ce-bc10-472b-a904-b0b17203f58a.png)

- utx提供了快速创建项目的能力，也就是脚手架。
```shell
utx startproject-web project_name
```
```text
$ utx startproject demo
2021-09-01 12:39:16.491 | INFO     | utx.cli.scaffold:create_scaffold:51 - Create new project: demo
Project root dir: /PycharmProjects/demo

Created folder: demo
Created folder: demo/config
Created folder: demo/logs
Created folder: demo/driver
Created folder: demo/report/airtest
Created folder: demo/tests
Created folder: demo/suites
Created file: demo/.gitignore
Created file: demo/conftest.py
Created file: demo/pytest.ini
Created file: demo/run.py
Created file: demo/requirements.txt
Created file: demo/config/conf.py
Created file: demo/config/config.ini
Created file: demo/config/__init__.py
Created file: demo/tests/test_devices.py
Created file: demo/tests/__init__.py
Created file: demo/report/summary_template.html

```
## 调用流程图

![](https://files.mdnice.com/user/17535/b6cf34a4-c973-412e-bbef-0d56d418e895.png)

## 专注于写脚本

- 项目结构很清晰。在conftest.py进行一些初始化/参数化/清理工作，在suites/写测试脚本。
>在AirtestIDE中写好.air脚本，然后将文件放到suites文件中。
- 更注重平铺写脚本的方式，这样就离“每个人都能用python写自动化”更近一步。毕竟封装之后看着容易晕，我也晕。
- 去除掉框架的约束，给每个人写python的自由，在测试脚本里你可以尽情发挥你的代码风格，代码能力，千人千面。代价呢，就是代码质量参差不齐。

大胆写，能写，写出来，跑通，就已经是在写自动化，就已经是在创造价值了！


## 轻封装

utx尊重原生用法。

airtest的封装只通过装饰器进行了运行方式的调整，没有做任何其他的冗余修改。

- faker，造数据工具
- pytest，测试框架
- selenium，web测试框架
- airtest，自动化测试工具
- pandas、numpy，数据处理工具

安装utx，自动就把这些开源利器安装上了，无需单独安装。未来会集成更多实用工具到utx中。

utx本身是很轻的。

## 功能介绍

1. 支持android,ios,web 平台的自动化测试框架
2. 脚本批量执行
3. 每个脚本执行日志分开存放
4. 每个脚本单独生成一个html报告并在父文件夹生成一个聚合报告
5. 自定义的聚合报告，详细展示运行结果
6. 重试机制，运行失败自动重跑，可自定义重跑次数
7. 自定义脚本运行，可选择部分模块单独运行
8. 自带脚手架工具可以快速生成框架目录


## 使用说明
- 下载好chrome浏览器对应的驱动，放到driver文件夹中
> 注意：文件名规范 【Linux/Mac：chromedriver】【Windows：chromedriver.exe】
- 只需在配置文件中填好相关内容，即可运行！
```ini
[web_info]
;被测的主页url
url = https://www.baidu.com/
;是否无界面运行
headless = False

[reruns]
;失败后再次运行次数，默认1次
times = 1

[mode]
;0表示 运行[suites][cases]选择的用例  1表示运行全部用例
is_all = 0

[suites]
;填写用例的关键字
cases = chrome

```


- 启动命令
1. 普通启动
```shell
python run.py
```
2. 参数化启动
```shell
python run.py --headless=True --driver=/Users/admin/driverpath
```
>参数优先级大于配置文件

## 运行结果
- 报告展示

![](https://files.mdnice.com/user/17535/29b7a536-9e30-45e3-b7ed-727a2091b910.png)

"""

    create_file(os.path.join(project_name, "README.md"), content)

    content = """#!/usr/bin/python
# encoding=utf-8

\"\"\" Can only be modified by the administrator. Only fixtures are provided.
\"\"\"

import pytest

from airtest.core.settings import Settings as ST
from utx.core.utils.hook import web_info, web_fixture
from config.conf import *
from run import cli_headless, cli_driver_path

ST.PROJECT_ROOT = BASE_DIR
is_headless, browser_driver_path = web_info(cli_headless=cli_headless,
                                            cli_driver=cli_driver_path,
                                            ini_headless=ini.getvalue('web_info', 'headless'),
                                            ini_driver=DRIVER_PATH)


@pytest.fixture(scope="session", autouse=True)
def web_init(request):
    driver = web_fixture(request, driver_path=browser_driver_path, headless=is_headless)
    return driver
"""
    create_file(os.path.join(project_name, "conftest.py"), content)

    content = """[pytest]
log_cli=true
log_cli_level=INFO
log_format = %(asctime)s [%(levelname)-5s] %(name)s %(funcName)s : %(message)s
log_date_format = %Y-%m-%d %H:%M:%S

# show all extra test summary info
addopts = -ra
testpaths = tests
python_files = test_browsers.py
"""
    create_file(os.path.join(project_name, "pytest.ini"), content)

    content = """#!/usr/bin/python
# encoding=utf-8
\"\"\" Can only be modified by the administrator. Only Runner are provided.
\"\"\"

import os
import pytest
from utx.cli.cli import cli_web_env

from config.conf import ALLURE_REPORT_PATH
from utx.core.utils.tools import allure_report

cli_headless, cli_driver_path = cli_web_env()

if __name__ == '__main__':
    report_path = ALLURE_REPORT_PATH + os.sep + "result"
    report_html_path = ALLURE_REPORT_PATH + os.sep + "html"
    pytest.main(["-s", "--alluredir", report_path, "--clean-alluredir"])
    allure_report(report_path, report_html_path)
"""

    create_file(os.path.join(project_name, "run.py"), content)

    content = """# Customize third-parties
# pip install --default-timeout=6000 -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt

"""
    create_file(os.path.join(project_name, "requirements.txt"), content)

    content = """#!/usr/bin/python
# encoding=utf-8
\"\"\" Can only be modified by the administrator. Only Path are provided.
\"\"\"
import os

# 项目目录
from utx.core.utils.tools import ReadConfig

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# 配置文件
INI_PATH = os.path.join(BASE_DIR, 'config', 'config.ini')

# 测试用例目录
CASE_PATH = os.path.join(BASE_DIR, 'suites')
if not os.path.exists(CASE_PATH):
    os.mkdir(CASE_PATH)
# 日志目录
LOG_PATH = os.path.join(BASE_DIR, 'logs')
if not os.path.exists(LOG_PATH):
    os.mkdir(LOG_PATH)
# 驱动目录
DRIVER_PATH = os.path.join(BASE_DIR, 'driver')
if not os.path.exists(DRIVER_PATH):
    os.mkdir(DRIVER_PATH)
# allure报告目录
ALLURE_REPORT_PATH = os.path.join(BASE_DIR, 'report')
if not os.path.exists(ALLURE_REPORT_PATH):
    os.mkdir(ALLURE_REPORT_PATH)

# airtest报告目录
AIRTEST_REPORT_PATH = os.path.join(BASE_DIR, 'report', 'airtest')
if not os.path.exists(AIRTEST_REPORT_PATH):
    os.mkdir(AIRTEST_REPORT_PATH)

ini = ReadConfig(INI_PATH)

if __name__ == '__main__':
    pass
"""

    create_file(os.path.join(project_name, "config", "conf.py"), content)

    content = """[web_info]
;被测的主页url
url = https://www.baidu.com/
;是否无界面运行
headless = False

[reruns]
;失败后再次运行次数，默认1次
times = 1

[mode]
;0表示 运行[suites][cases]选择的用例  1表示运行全部用例
is_all = 0

[suites]
;填写用例的关键字
cases = chrome
"""
    create_file(os.path.join(project_name, "config", "config.ini"), content)
    create_file(os.path.join(project_name, "config", "__init__.py"))

    content = """#!/usr/bin/python
# encoding=utf-8
\"\"\" Can only be modified by the administrator. Only Cases are provided.
\"\"\"

import allure
import pytest
from utx.core.css.custom_report import gen_report
from utx.core.utils.tools import decryption, selector

from config.conf import *
from utx.core.utils.runner import run_web_air

cases_list = sorted(os.listdir(CASE_PATH))
results = []
result_list = []

status = ini.getvalue('mode', 'is_all')
flag = ini.getvalue('suites', 'cases')
home_url = ini.getvalue('web_info', 'url')
times = int(ini.getvalue('reruns', 'times'))
cases = selector(status=status, flag=flag, cases_list=cases_list)


@allure.feature("Web automation test")
class TestWeb:
    @pytest.mark.flaky(reruns=times)
    @pytest.mark.parametrize("case", cases)
    @allure.story("Web automation test")
    @allure.title("Current browser:chrome,Running case:{case}")
    @allure.description("APP automation test")
    @allure.severity(allure.severity_level.TRIVIAL)
    @allure.tag("Smoke testing")
    def test_ui_web(self, case, web_init):
        driver = web_init
        driver.get(home_url)
        result, dev = run_web_air(case=case, log_path=LOG_PATH, case_path=CASE_PATH,
                                  base_dir=BASE_DIR,
                                  static_dir=decryption('aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2doL29wZW51dHgvc3RhdGljLw=='))
        result_list.append(result)
        summary_info = {'devices': dev, 'result': result_list}
        results.append(summary_info)
        assert result['result'] is True

    @allure.story("Web automation test")
    @allure.title("Generate test report of running device")
    @allure.description("Web automation test")
    @allure.severity(allure.severity_level.TRIVIAL)
    @allure.tag("Generate report")
    def test_report(self):
        print(results)
        gen_report(results=results, report_path=AIRTEST_REPORT_PATH)


if __name__ == "__main__":
    pytest.main(["-s", "test_browsers.py"])
"""

    create_file(os.path.join(project_name, "tests", "test_browsers.py"), content)
    create_file(os.path.join(project_name, "tests", "__init__.py"))

    if ExtraArgument.create_venv:
        os.chdir(project_name)
        print("\nCreating virtual environment")
        os.system("python -m venv venv")
        print("Created virtual environment: venv")

        print("Installing utx")
        if platform.system().lower() == 'windows':
            os.chdir("venv")
            os.chdir("Scripts")
            os.system("pip install utx")
        elif platform.system().lower() == 'linux':
            os.chdir("venv")
            os.chdir("bin")
            os.system("pip install utx")
        elif platform.system().lower() == 'mac':
            os.chdir("venv")
            os.chdir("bin")
            os.system("pip install utx")


def main_scaffold_web(args):
    ExtraArgument.create_venv = args.create_venv
    sys.exit(create_scaffold_web(args.project_name))
