init: базовая структура АТ
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
allure-results/
|
||||
allure-report/
|
||||
test-results/
|
||||
.env
|
||||
38
README.md
Normal file
38
README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
# Autotests: DigitalTwin Platform
|
||||
|
||||
Автоматизированные UI и API тесты для проекта **DigitalTwin**. Используется стек:
|
||||
- [Playwright](https://playwright.dev/) + TypeScript
|
||||
- Axios для API-запросов
|
||||
- Allure для формирования отчётов
|
||||
|
||||
## 📂 Структура проекта
|
||||
|
||||
```
|
||||
.
|
||||
├── tests/
|
||||
│ ├── ui/ # UI-тесты (Playwright)
|
||||
│ └── api/ # API-тесты (Axios + Playwright)
|
||||
├── utils/ # Генераторы данных, вспомогательные утилиты
|
||||
├── package.json # Скрипты запуска
|
||||
├── tsconfig.json # Конфигурация TypeScript
|
||||
├── playwright.config.ts
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
## 🚀 Скрипты
|
||||
|
||||
| Скрипт | Назначение |
|
||||
|------------------------|--------------------------------------------|
|
||||
| `npm run test` | Полный запуск тестов |
|
||||
| `npm run test:ui` | Только UI-тесты |
|
||||
| `npm run test:api` | Только API-тесты |
|
||||
| `npm run report` | Открыть HTML-отчёт Playwright |
|
||||
| `npm run allure:report`| Сгенерировать и открыть Allure отчёт |
|
||||
| `npm run test:clean` | Удалить старые отчёты Allure |
|
||||
|
||||
## 🛠️ Установка
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
447
package-lock.json
generated
Normal file
447
package-lock.json
generated
Normal file
@@ -0,0 +1,447 @@
|
||||
{
|
||||
"name": "digitaltwin",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "digitaltwin",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.13.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.57.0",
|
||||
"allure-playwright": "^3.4.5",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.57.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz",
|
||||
"integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.57.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/allure-js-commons": {
|
||||
"version": "3.4.5",
|
||||
"resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.4.5.tgz",
|
||||
"integrity": "sha512-mzAppLFva9PJqWvdI/aXn8jqr+5Am67JITdthMfEk8En6AhsrvRWZAjHZ1yUMDGEbxmivCni3lppvitJGStxFQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"md5": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"allure-playwright": "3.4.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"allure-playwright": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/allure-playwright": {
|
||||
"version": "3.4.5",
|
||||
"resolved": "https://registry.npmjs.org/allure-playwright/-/allure-playwright-3.4.5.tgz",
|
||||
"integrity": "sha512-pVewTpU9Z4qgT14VJdtYLAfF8rWROuESmvDkvyu/QnFWhRFrcDBnomynj84yx/QpXyMjJL+qu1yMU2z4Mq1YnA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"allure-js-commons": "3.4.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@playwright/test": ">=1.53.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.3.tgz",
|
||||
"integrity": "sha512-ERT8kdX7DZjtUm7IitEyV7InTHAF42iJuMArIiDIV5YtPanJkgw4hw5Dyg9fh0mihdWNn1GKaeIWErfe56UQ1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/charenc": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
|
||||
"integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypt": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
||||
"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/md5": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
|
||||
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"charenc": "0.0.2",
|
||||
"crypt": "0.0.2",
|
||||
"is-buffer": "~1.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.57.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz",
|
||||
"integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.57.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.57.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz",
|
||||
"integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
package.json
Normal file
26
package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "digitaltwin",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "npx playwright test",
|
||||
"test:clean": "rm -rf allure-results allure-report",
|
||||
"test:ui": "npx playwright test tests/ui",
|
||||
"test:api": "npx playwright test tests/api",
|
||||
"report": "npx playwright show-report",
|
||||
"allure:report": "allure generate ./allure-results --clean -o ./allure-report && allure open ./allure-report"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Vlad Smykov",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.57.0",
|
||||
"allure-playwright": "^3.4.5",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.13.3"
|
||||
}
|
||||
}
|
||||
55
page-objects/RegisterPage.ts
Normal file
55
page-objects/RegisterPage.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
// page-objects/RegisterPage.ts
|
||||
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
export class RegisterPage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/registration');
|
||||
}
|
||||
|
||||
async fillLastName(lastName: string) {
|
||||
await this.page.fill('input[name="surname"]', lastName);
|
||||
}
|
||||
|
||||
async fillFirstName(firstName: string) {
|
||||
await this.page.fill('input[name="name"]', firstName);
|
||||
}
|
||||
|
||||
async fillMiddleName(middleName: string) {
|
||||
await this.page.fill('input[name="patronymic"]', middleName);
|
||||
}
|
||||
|
||||
async fillEmail(email: string) {
|
||||
await this.page.fill('input[name="email"]', email);
|
||||
}
|
||||
|
||||
async fillLogin(login: string) {
|
||||
await this.page.fill('input[name="login"]', login);
|
||||
}
|
||||
|
||||
async fillPhone(phone: string) {
|
||||
await this.page.fill('input[name="phone"]', phone);
|
||||
}
|
||||
|
||||
async fillPassword(password: string) {
|
||||
await this.page.fill('input[name="password"]', password);
|
||||
}
|
||||
|
||||
async fillPasswordRepeat(password: string) {
|
||||
await this.page.fill('input[name="passwordRepeat"]', password);
|
||||
}
|
||||
|
||||
async checkConsentCheckbox() {
|
||||
await this.page.locator('span.Checkmark_checkmark__58fWm').click();
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await this.page.locator('button[data-testid="btn-save"]').click();
|
||||
}
|
||||
}
|
||||
85
playwright-report/index.html
Normal file
85
playwright-report/index.html
Normal file
File diff suppressed because one or more lines are too long
25
playwright.config.ts
Normal file
25
playwright.config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineConfig } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './tests',
|
||||
timeout: 30000,
|
||||
expect: {
|
||||
timeout: 5000
|
||||
},
|
||||
reporter: [
|
||||
['html', { outputFolder: 'playwright-report', open: 'never' }],
|
||||
['list'],
|
||||
['allure-playwright']
|
||||
],
|
||||
use: {
|
||||
headless: false,
|
||||
launchOptions: {
|
||||
slowMo: 300,
|
||||
},
|
||||
viewport: { width: 1280, height: 720 },
|
||||
ignoreHTTPSErrors: true,
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'retain-on-failure',
|
||||
baseURL: 'https://rumc.dev.rdcenter.ru'
|
||||
}
|
||||
});
|
||||
62
tests/api/registration/register-applicant.api.spec.ts
Normal file
62
tests/api/registration/register-applicant.api.spec.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
createTempEmail,
|
||||
waitForConfirmationCode
|
||||
} from '../../../utils/mailTmApi';
|
||||
import {
|
||||
generateFirstName,
|
||||
generateLastName,
|
||||
generateMiddleName,
|
||||
generateLogin,
|
||||
generatePhone,
|
||||
} from '../../../utils/userGenerator';
|
||||
|
||||
const BASE_URL = 'https://rumc.dev.rdcenter.ru/api';
|
||||
|
||||
test('API: регистрация абитуриента + подтверждение почты', async () => {
|
||||
const { email, token: mailToken } = await createTempEmail();
|
||||
|
||||
const surname = generateLastName();
|
||||
const name = generateFirstName();
|
||||
const patronymic = generateMiddleName();
|
||||
const fullName = `${surname} ${name} ${patronymic}`;
|
||||
const login = generateLogin();
|
||||
const phone = '+7 (900) 000-00-00';
|
||||
|
||||
const registerPayload = {
|
||||
role: 'APPLICANT',
|
||||
type: 'INDIVIDUAL',
|
||||
login,
|
||||
fullName,
|
||||
email,
|
||||
password: '!Test123456',
|
||||
phone,
|
||||
approval: true
|
||||
};
|
||||
|
||||
//const registerRes = await axios.post(`${BASE_URL}/auth/register`, registerPayload);
|
||||
//expect(registerRes.status).toBe(201);
|
||||
try {
|
||||
const registerRes = await axios.post(`${BASE_URL}/auth/register`, registerPayload);
|
||||
expect(registerRes.status).toBe(201);
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('❌ Ошибка регистрации:', error.response?.data || error.message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.log('📬 Ожидание письма с кодом подтверждения...');
|
||||
const code = await waitForConfirmationCode(mailToken, 60000);
|
||||
console.log('✅ Код получен:', code);
|
||||
|
||||
const confirmPayload = {
|
||||
email,
|
||||
code
|
||||
};
|
||||
|
||||
const confirmRes = await axios.post(`${BASE_URL}/auth/confirm`, confirmPayload);
|
||||
expect(confirmRes.status).toBe(200);
|
||||
|
||||
console.log('🎉 Почта подтверждена успешно');
|
||||
});
|
||||
54
tests/ui/registration/register-applicant.spec.ts
Normal file
54
tests/ui/registration/register-applicant.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
// Позитивный тест регистрации абитуриента
|
||||
// Включает в себя:
|
||||
// Ввод валидных тестовых данных
|
||||
// Получение кода в письме
|
||||
// Ввод кода из письма (is_confirm)
|
||||
// Переход на главную
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { RegisterPage } from '../../../page-objects/RegisterPage';
|
||||
import {
|
||||
generateFirstName,
|
||||
generateLastName,
|
||||
generateMiddleName,
|
||||
generateLogin,
|
||||
generatePhone,
|
||||
} from '../../../utils/userGenerator';
|
||||
import {
|
||||
createTempEmail,
|
||||
waitForConfirmationCode
|
||||
} from '../../../utils/mailTmApi';
|
||||
|
||||
test('Полная регистрация абитуриента с подтверждением почты', async ({ page }) => {
|
||||
const registerPage = new RegisterPage(page);
|
||||
|
||||
const { email, token } = await createTempEmail();
|
||||
|
||||
const firstName = generateFirstName();
|
||||
const lastName = generateLastName();
|
||||
const middleName = generateMiddleName();
|
||||
const login = generateLogin();
|
||||
const phone = generatePhone();
|
||||
const password = '!Test123456';
|
||||
|
||||
await registerPage.goto();
|
||||
await registerPage.fillLastName(lastName);
|
||||
await registerPage.fillFirstName(firstName);
|
||||
await registerPage.fillMiddleName(middleName);
|
||||
await registerPage.fillEmail(email);
|
||||
await registerPage.fillLogin(login);
|
||||
await registerPage.fillPhone(phone);
|
||||
await registerPage.fillPassword(password);
|
||||
await registerPage.fillPasswordRepeat(password);
|
||||
await registerPage.checkConsentCheckbox();
|
||||
await registerPage.submit();
|
||||
|
||||
await expect(page).toHaveURL(/confirmation-code/);
|
||||
const code = await waitForConfirmationCode(token, 60000);
|
||||
|
||||
await page.fill('input[name="code"]', code);
|
||||
await page.click('button.RecoverPassword_button__5QDxM');
|
||||
|
||||
await expect(page).toHaveURL('https://rumc.dev.rdcenter.ru');
|
||||
|
||||
});
|
||||
74
utils/mailTmApi.ts
Normal file
74
utils/mailTmApi.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
// utils/mailTmApi.ts
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
const MAIL_TM_BASE = 'https://api.mail.tm';
|
||||
|
||||
export async function createTempEmail() {
|
||||
const timestamp = Date.now();
|
||||
|
||||
// Получаем доступный домен
|
||||
const domainRes = await axios.get(`${MAIL_TM_BASE}/domains`);
|
||||
const domain = domainRes.data['hydra:member'][0].domain;
|
||||
|
||||
const email = `testuser${timestamp}@${domain}`;
|
||||
const password = 'QwEr!1234567';
|
||||
|
||||
// Регистрируем
|
||||
await axios.post(`${MAIL_TM_BASE}/accounts`, {
|
||||
address: email,
|
||||
password,
|
||||
});
|
||||
|
||||
// Авторизуемся
|
||||
const tokenRes = await axios.post(`${MAIL_TM_BASE}/token`, {
|
||||
address: email,
|
||||
password,
|
||||
});
|
||||
|
||||
const token = tokenRes.data.token;
|
||||
|
||||
return { email, password, token };
|
||||
}
|
||||
|
||||
|
||||
export async function waitForConfirmationCode(token: string, timeout = 60000) {
|
||||
const start = Date.now();
|
||||
|
||||
while (Date.now() - start < timeout) {
|
||||
const res = await axios.get(`${MAIL_TM_BASE}/messages`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
const messages = res.data['hydra:member'];
|
||||
|
||||
// ✅ Логируем полученные письма
|
||||
if (messages.length === 0) {
|
||||
console.log('⏳ Письма пока не пришли...');
|
||||
} else {
|
||||
console.log(`📬 Получено ${messages.length} писем:`);
|
||||
}
|
||||
|
||||
for (const message of messages) {
|
||||
console.log('🔖 Тема письма:', message.subject);
|
||||
|
||||
const msg = await axios.get(`${MAIL_TM_BASE}/messages/${message.id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
const body = msg.data.text || msg.data.html;
|
||||
console.log('📩 Тело письма:', body);
|
||||
|
||||
const match = body.match(/\b\d{6}\b/);
|
||||
if (match) {
|
||||
console.log('✅ Код подтверждения найден:', match[0]);
|
||||
return match[0];
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise(res => setTimeout(res, 2000)); // Пауза 2 сек
|
||||
}
|
||||
|
||||
throw new Error('Код подтверждения не получен');
|
||||
}
|
||||
|
||||
45
utils/userGenerator.ts
Normal file
45
utils/userGenerator.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
// utils/userGenerator.ts
|
||||
|
||||
// Список русских имён, фамилий, отчеств
|
||||
const firstNames = ['Алексей', 'Игорь', 'Иван', 'Владислав', 'Дмитрий', 'Константин', 'Сергей', 'Всеволод'];
|
||||
const lastNames = ['Иванов', 'Петров', 'Сидоров', 'Кузнецов', 'Смирнов', 'Попов', 'Фёдоров'];
|
||||
const middleNames = ['Алексеевич', 'Иванович', 'Дмитриевич', 'Андреевич', 'Сергеевич', 'Олегович'];
|
||||
|
||||
function getRandomFromArray(array: string[]): string {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
export function generateFirstName(): string {
|
||||
return getRandomFromArray(firstNames);
|
||||
}
|
||||
|
||||
export function generateLastName(): string {
|
||||
return getRandomFromArray(lastNames);
|
||||
}
|
||||
|
||||
export function generateMiddleName(): string {
|
||||
return getRandomFromArray(middleNames);
|
||||
}
|
||||
|
||||
export function generateRandomString(length: number): string {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function generateEmail(): string {
|
||||
const timestamp = Date.now();
|
||||
return `test_user_${timestamp}@mail.tm`;
|
||||
}
|
||||
|
||||
export function generateLogin(): string {
|
||||
return `user_${generateRandomString(6)}`;
|
||||
}
|
||||
|
||||
export function generatePhone(): string {
|
||||
const random = Math.floor(100000000 + Math.random() * 900000000); // 9 цифр
|
||||
return `+790${random}`; // +79012345678
|
||||
}
|
||||
Reference in New Issue
Block a user