This commit is contained in:
commit
dc7a816777
27
.github/workflows/playwright.yml
vendored
Normal file
27
.github/workflows/playwright.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
name: Playwright Tests
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
pull_request:
|
||||||
|
branches: [main, master]
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
timeout-minutes: 60
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: lts/*
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: Install Playwright Browsers
|
||||||
|
run: npx playwright install --with-deps
|
||||||
|
- name: Run Playwright tests
|
||||||
|
run: npx playwright test
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: playwright-report/
|
||||||
|
retention-days: 30
|
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Distribution
|
||||||
|
dist
|
||||||
|
out
|
||||||
|
build
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log*
|
||||||
|
logs
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"deno.enable": true,
|
||||||
|
"deno.lint": true
|
||||||
|
}
|
22
README.md
Normal file
22
README.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# SAP BAS dev space keep-alive
|
||||||
|
|
||||||
|
This script will start a SAP BAS dev space and keep it running.
|
||||||
|
|
||||||
|
It uses Playwright for browser automation and Deno 2 as a runtime.
|
||||||
|
|
||||||
|
You can use SSH port forwarding with the started dev space to access SAP proxy locally for
|
||||||
|
remote system access for a true local development.
|
||||||
|
|
||||||
|
## Setup and usage
|
||||||
|
|
||||||
|
Copy the `example.env` to `.env` and fill in the necessary details for BAS access.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
brew install deno
|
||||||
|
deno run -A npm:playwright install
|
||||||
|
./run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
Set `DENO_HEADLESS=false` to see the browser as the actions are taken.
|
16
deno.json
Normal file
16
deno.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"dev": "deno run --watch main.ts"
|
||||||
|
},
|
||||||
|
"imports": {
|
||||||
|
"@std/assert": "jsr:@std/assert@1"
|
||||||
|
},
|
||||||
|
"nodeModulesDir": "auto",
|
||||||
|
"fmt": {
|
||||||
|
"useTabs": false,
|
||||||
|
"lineWidth": 90,
|
||||||
|
"indentWidth": 2,
|
||||||
|
"semiColons": false,
|
||||||
|
"singleQuote": false
|
||||||
|
}
|
||||||
|
}
|
56
deno.lock
generated
Normal file
56
deno.lock
generated
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"version": "4",
|
||||||
|
"specifiers": {
|
||||||
|
"jsr:@std/assert@1": "1.0.7",
|
||||||
|
"jsr:@std/internal@^1.0.5": "1.0.5",
|
||||||
|
"npm:@playwright/test@*": "1.48.2",
|
||||||
|
"npm:@types/node@*": "22.5.4",
|
||||||
|
"npm:playwright@*": "1.48.2"
|
||||||
|
},
|
||||||
|
"jsr": {
|
||||||
|
"@std/assert@1.0.7": {
|
||||||
|
"integrity": "64ce9fac879e0b9f3042a89b3c3f8ccfc9c984391af19e2087513a79d73e28c3",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/internal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/internal@1.0.5": {
|
||||||
|
"integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm": {
|
||||||
|
"@playwright/test@1.48.2": {
|
||||||
|
"integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==",
|
||||||
|
"dependencies": [
|
||||||
|
"playwright"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@types/node@22.5.4": {
|
||||||
|
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
|
||||||
|
"dependencies": [
|
||||||
|
"undici-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fsevents@2.3.2": {
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="
|
||||||
|
},
|
||||||
|
"playwright-core@1.48.2": {
|
||||||
|
"integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA=="
|
||||||
|
},
|
||||||
|
"playwright@1.48.2": {
|
||||||
|
"integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==",
|
||||||
|
"dependencies": [
|
||||||
|
"fsevents",
|
||||||
|
"playwright-core"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"undici-types@6.19.8": {
|
||||||
|
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert@1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
9
example.env
Normal file
9
example.env
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
BAS_URL=https://yolo.eu10cf.applicationstudio.cloud.sap
|
||||||
|
|
||||||
|
BAS_PROFILE=yoloswag.accounts.ondemand.com
|
||||||
|
#BAS_PROFILE=Default Identity Provider
|
||||||
|
|
||||||
|
BAS_USERNAME=user.name@company.lol
|
||||||
|
BAS_PASSWORD=
|
||||||
|
|
||||||
|
BAS_DEVSPACE=dev
|
142
main.ts
Normal file
142
main.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import playwright from "npm:playwright"
|
||||||
|
|
||||||
|
const devSpace = Deno.env.get("BAS_DEVSPACE")!
|
||||||
|
const headless = Deno.env.get("DENO_HEADLESS")! === "true" ? true : false
|
||||||
|
|
||||||
|
if (import.meta.main) {
|
||||||
|
while (true) {
|
||||||
|
console.log(`Starting new loop`)
|
||||||
|
await main(devSpace)
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 3 * 1000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main(devSpacename: string) {
|
||||||
|
const browser = await playwright.chromium.launch({
|
||||||
|
headless: headless,
|
||||||
|
})
|
||||||
|
const context = await browser.newContext()
|
||||||
|
const page = await context.newPage()
|
||||||
|
|
||||||
|
console.log(`Logging in to BAS`)
|
||||||
|
await page.goto(Deno.env.get("BAS_URL")!)
|
||||||
|
await page.locator(`text=${Deno.env.get("BAS_PROFILE")!}`).click()
|
||||||
|
await page
|
||||||
|
.getByLabel("Email or User Name")
|
||||||
|
.fill(Deno.env.get("BAS_USERNAME")!)
|
||||||
|
await page.getByLabel("Password").fill(Deno.env.get("BAS_PASSWORD")!)
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.getByText("Keep me signed in").check()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.getByText("Continue").click()
|
||||||
|
await page.waitForLoadState()
|
||||||
|
await page.waitForURL("**/index.html")
|
||||||
|
|
||||||
|
// Request manager page directly (without iframe)
|
||||||
|
console.log(`Loading management page`)
|
||||||
|
await page.goto(Deno.env.get("BAS_URL")! + "/workspace-manager-ui/")
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
console.log(`Loaded management page`)
|
||||||
|
|
||||||
|
// const pageSource = await page.content();
|
||||||
|
// console.log(JSON.stringify(pageSource));
|
||||||
|
|
||||||
|
const devSpaceEl = await findCorrectDevspace(page, devSpacename)
|
||||||
|
if (!devSpaceEl) {
|
||||||
|
throw Error(`Could not find defined devspace: ${devSpacename}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
await startDevSpace(devSpaceEl, page)
|
||||||
|
|
||||||
|
await loadVSCode(devSpaceEl, devSpacename)
|
||||||
|
|
||||||
|
console.log(`Waiting for 5 minutes and then starting again...`)
|
||||||
|
await page.waitForTimeout(5 * 60 * 1000)
|
||||||
|
|
||||||
|
// Close on done
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
page.on("close", resolve) // <-- add this
|
||||||
|
})
|
||||||
|
|
||||||
|
await context.close()
|
||||||
|
await browser.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findCorrectDevspace(
|
||||||
|
page: playwright.Page,
|
||||||
|
devSpacename: string,
|
||||||
|
) {
|
||||||
|
const rows = await page
|
||||||
|
.locator('div.dev-spaces-row:has-text("' + devSpacename + '")')
|
||||||
|
.all()
|
||||||
|
|
||||||
|
let findFullStack = false
|
||||||
|
if (rows.length > 1) {
|
||||||
|
console.log(`Found multiple "${devSpacename}" dev spaces`)
|
||||||
|
findFullStack = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const el of rows) {
|
||||||
|
console.log(`Checking dev space row`)
|
||||||
|
const fullstack = await el
|
||||||
|
.locator(
|
||||||
|
'div.left-row:has-text("Full-Stack Application Using Productivity Tools")',
|
||||||
|
)
|
||||||
|
.isVisible()
|
||||||
|
|
||||||
|
if (fullstack) {
|
||||||
|
console.log(`Found the fullstack dev space row`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip non-fullstack lines if they exist for that name
|
||||||
|
if (findFullStack && !fullstack) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startDevSpace(
|
||||||
|
devSpaceEl: playwright.Locator,
|
||||||
|
page: playwright.Page,
|
||||||
|
) {
|
||||||
|
const running = await devSpaceEl
|
||||||
|
.locator('div.text-center a:has-text("RUNNING")')
|
||||||
|
.isVisible()
|
||||||
|
|
||||||
|
if (running) {
|
||||||
|
console.log(`Dev Space running`)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopped = await devSpaceEl
|
||||||
|
.locator('div.text-center a:has-text("STOPPED")')
|
||||||
|
.isVisible()
|
||||||
|
|
||||||
|
if (stopped) {
|
||||||
|
await devSpaceEl.locator('button[id^="startButton"]').click()
|
||||||
|
await page.waitForTimeout(5 * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopping = await devSpaceEl
|
||||||
|
.locator('div.text-center a:has-text("STOPPING")')
|
||||||
|
.isVisible()
|
||||||
|
|
||||||
|
const starting = await devSpaceEl
|
||||||
|
.locator('div.text-center a:has-text("STARTING")')
|
||||||
|
.isVisible()
|
||||||
|
|
||||||
|
if (stopping || starting) {
|
||||||
|
await page.waitForTimeout(2 * 1000)
|
||||||
|
console.log(`Waiting for dev space to ${stopping ? "stop" : "start"}...`)
|
||||||
|
await startDevSpace(devSpaceEl, page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadVSCode(
|
||||||
|
devSpaceEl: playwright.Locator,
|
||||||
|
devSpacename: string,
|
||||||
|
) {
|
||||||
|
console.log(`Opening VSCode dev space`)
|
||||||
|
await devSpaceEl.locator('a:has-text("' + devSpacename + '")').click()
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user