Skip to content

Commit

Permalink
Selenium を Playwright に置き換え
Browse files Browse the repository at this point in the history
  • Loading branch information
nanasess committed Dec 23, 2021
1 parent 826d983 commit 8f73fdb
Show file tree
Hide file tree
Showing 9 changed files with 968 additions and 2,048 deletions.
3 changes: 1 addition & 2 deletions .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"useBuiltIns": "usage",
"corejs": 2
}
],
"@babel/preset-typescript"
]
]
}
27 changes: 15 additions & 12 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,18 @@ jobs:
docker-compose exec -T postgres psql --user=eccube_db_user eccube_db -c "UPDATE dtb_customer SET email = '[email protected]' WHERE customer_id = (SELECT MAX(customer_id) FROM dtb_customer WHERE status = 2 AND del_flg = 0);"
- run: sleep 1
- run: yarn install
- run: |
yarn install
yarn run playwright install --with-deps chromium
yarn playwright install-deps chromium
- name: Run to E2E testing
env:
GROUP: ${{ matrix.group }}
HTTPS_PROXY: 'localhost:8090'
ZAP_PROXY_HOST: 'localhost:8090'
CI: 1
FORCE_COLOR: 1
# Use runner's chromedriver to run on the latest version of google-chrome
run: yarn test:e2e e2e-tests/${GROUP}

Expand Down Expand Up @@ -102,9 +108,6 @@ jobs:
env:
DB_TYPE: ${{ matrix.db }}
run: echo "COMPOSE_FILE=docker-compose.yml:docker-compose.${DB_TYPE}.yml:docker-compose.dev.yml:docker-compose.owaspzap.yml:docker-compose.owaspzap.daemon.yml" >> $GITHUB_ENV
- run: |
ls -al data/config
ls -al html/upload/save_image
- name: Setup to EC-CUBE
env:
HTTP_URL: https://127.0.0.1:8085/
Expand All @@ -113,23 +116,23 @@ jobs:
run: |
sudo chown -R 1001:1000 zap
sudo chmod -R g+w zap
touch data/config/config.php
ls -al data/config
ls -al html/upload/save_image
sh -c 'echo "<?php" >> data/config/config.php'
docker-compose build --build-arg TAG=${TAG} ec-cube
docker-compose up -d
rm data/config/config.php
- run: sleep 1
- run: yarn install
- run: |
ls -al data/config
ls -al html/upload/save_image
docker-compose exec -T ec-cube ls -al /var/www/app/data/config
docker-compose exec -T ec-cube ls -al /var/www/app/html/upload/save_image
yarn install
yarn run playwright install --with-deps chromium
yarn playwright install-deps chromium
- name: Run to E2E testing
env:
ZAP_PROXY_HOST: 'localhost:8090'
HTTPS_PROXY: 'localhost:8090'
CI: 1
FORCE_COLOR: 1
DB_TYPE: ${{ matrix.db }}
DB_USER: ${{ matrix.dbuser }}
DB_PASSWORD: ${{ matrix.dbpass }}
Expand Down
216 changes: 110 additions & 106 deletions e2e-tests/test/front_login/contact.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { Builder, By, until } from 'selenium-webdriver'
import { ZapClient, Mode, ContextType, Risk } from '../../utils/ZapClient';
import { test, expect, chromium, Page } from '@playwright/test';
import { ZapClient, Mode, ContextType, Risk, HttpMessage } from '../../utils/ZapClient';
import { intervalRepeater } from '../../utils/Progress';
import { SeleniumCapabilities } from '../../utils/SeleniumCapabilities';
const zapClient = new ZapClient();


jest.setTimeout(6000000);

const inputNames = [
'name01', 'name02', 'kana01', 'kana02', 'zip01', 'zip02', 'addr01', 'addr02',
'tel01', 'tel02', 'tel03'
Expand All @@ -16,121 +12,129 @@ type InputName = {
};

const baseURL = 'https://ec-cube';
const url = baseURL + '/contact/';
const url = baseURL + '/contact/index.php';

test.describe.serial('お問い合わせページのテストをします', () => {
let page: Page;
test.beforeAll(async () => {
await zapClient.setMode(Mode.Protect);
await zapClient.newSession('/zap/wrk/sessions/front_login_contact', true);
await zapClient.importContext(ContextType.FrontLogin);

if (!await zapClient.isForcedUserModeEnabled()) {
await zapClient.setForcedUserModeEnabled();
expect(await zapClient.isForcedUserModeEnabled()).toBeTruthy();
}
const browser = await chromium.launch();
page = await browser.newPage();
await page.goto(url);
});

beforeAll(async () => {
await zapClient.setMode(Mode.Protect);
await zapClient.newSession('/zap/wrk/sessions/front_login_contact', true);
await zapClient.importContext(ContextType.FrontLogin);
test('お問い合わせページを表示します', async () => {
await expect(page).toHaveTitle(//);
await expect(page.locator('h2.title')).toContainText('お問い合わせ');
});

if (!await zapClient.isForcedUserModeEnabled()) {
await zapClient.setForcedUserModeEnabled();
expect(await zapClient.isForcedUserModeEnabled()).toBeTruthy();
}
});
test.describe('テストを実行します[GET] @attack', () => {
let scanId: number;
test('アクティブスキャンを実行します', async () => {
scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'GET');
await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000, page);
});

describe('お問い合わせページを表示する', () => {
test('[E2E] お問い合わせページを表示し、ログイン状態を確認する', async () => {
const driver = await new Builder()
.withCapabilities(SeleniumCapabilities)
.build();
try {
expect.assertions(15);
await driver.get(url);
await driver.wait(
until.elementLocated(By.css('h2.title')), 10000)
.getText().then(title => expect(title).toBe('お問い合わせ(入力ページ)'));

inputNames.forEach(
async (name) => await driver.findElement(By.name(name)).getAttribute('value')
.then(value => expect(value).toEqual(expect.anything()))
);
await driver.findElement(By.name('email')).getAttribute('value').then(value => expect(value).toBe('[email protected]'));
await driver.findElement(By.name('email02')).getAttribute('value').then(value => expect(value).toBe('[email protected]'));

} finally {
driver && await driver.quit();
}
test('結果を確認します', async () => {
await zapClient.getAlerts(url, 0, 1, Risk.High)
.then(alerts => expect(alerts).toEqual([]));
});
});

describe('[ATTACK] お問い合わせページの表示をスキャンする', () => {
test('GET でお問い合わせページをスキャンする', async () => {
const scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'GET');
test('ログイン状態を確認します', async () => {
await page.goto(baseURL); // ログアウトしてしまう場合があるので一旦トップへ遷移する
await page.goto(url);
await expect(page.locator('#header')).toContainText('ようこそ');
inputNames.forEach(async (name) => expect(page.locator(`input[name=${name}]`)).not.toBeEmpty());
await expect(page.locator('input[name=email]')).toHaveValue('[email protected]');
await expect(page.locator('input[name=email02]')).toHaveValue('[email protected]');
});

await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000);
let confirmMessage: HttpMessage;
test('お問い合わせ内容を入力します', async () => {
await page.fill('textarea[name=contents]', 'お問い合わせ入力');
await page.click('input[name=confirm]');
confirmMessage = await zapClient.getLastMessage(url);
});

const alerts = await zapClient.getAlerts(url, 0, 1, Risk.High);
alerts.forEach(alert => {
throw new Error(alert.name);
});
expect(alerts).toHaveLength(0);
test('入力内容を確認します', async () => {
await expect(page.locator('h2.title')).toContainText('お問い合わせ(確認ページ)');
inputNames.forEach(async (name) => {
await expect(page.locator(`input[name=${name}]`)).toBeHidden();
await expect(page.locator(`input[name=${name}]`)).not.toBeEmpty();
});
await expect(page.locator('input[name=email]')).toBeHidden();
await expect(page.locator('input[name=email]')).toHaveValue('[email protected]');
await expect(page.locator('input[name=contents]')).toBeHidden();
await expect(page.locator('input[name=contents]')).toHaveValue('お問い合わせ入力');

await expect(page.locator('#form1 >> tr:nth-child(1) > td')).toContainText(await page.locator('input[name=name01]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(1) > td')).toContainText(await page.locator('input[name=name02]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(2) > td')).toContainText(await page.locator('input[name=kana01]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(2) > td')).toContainText(await page.locator('input[name=kana02]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(3) > td')).toContainText(await page.locator('input[name=zip01]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(3) > td')).toContainText(await page.locator('input[name=zip02]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(4) > td')).toContainText(await page.locator('input[name=addr01]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(4) > td')).toContainText(await page.locator('input[name=addr02]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(5) > td')).toContainText(await page.locator('input[name=tel01]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(5) > td')).toContainText(await page.locator('input[name=tel02]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(5) > td')).toContainText(await page.locator('input[name=tel03]').inputValue());
await expect(page.locator('#form1 >> tr:nth-child(6) > td')).toContainText('[email protected]');
await expect(page.locator('#form1 >> tr:nth-child(7) > td')).toContainText('お問い合わせ入力');
});
});

describe('お問い合わせ確認ページを表示する', () => {
test('[E2E] お問い合わせページに入力し、確認画面に進む', async () => {
const driver = await new Builder()
.withCapabilities(SeleniumCapabilities)
.build();
try {
await driver.get(url);
await driver.wait(
until.elementLocated(By.css('h2.title')), 10000)
.getText().then(title => expect(title).toBe('お問い合わせ(入力ページ)'));

// 入力値を代入しておく
let inputField: InputName = {};
inputNames.forEach(
async (name) => await driver.findElement(By.name(name)).getAttribute('value')
.then(value => inputField[name] = value)
);

await driver.findElement(By.name('contents')).sendKeys('お問い合わせ内容入力');
await driver.findElement(By.name('confirm')).click();

await driver.wait(
until.elementLocated(By.css('h2.title')), 10000)
.getText().then(title => expect(title).toBe('お問い合わせ(確認ページ)'));

// hidden に入力されているかどうか
inputNames.forEach(
async (name) => await driver.findElement(By.name(name)).getAttribute('value')
.then(value => expect(value).toBe(inputField[name]))
);
// 確認画面に表示されているかどうか
await driver.findElement(By.xpath('//*[@id="form1"]/table/tbody/tr[1]/td')).getText()
.then(value => expect(value).toBe(`${inputField.name01} ${inputField.name02}`));
await driver.findElement(By.xpath('//*[@id="form1"]/table/tbody/tr[2]/td')).getText()
.then(value => expect(value).toBe(`${inputField.kana01} ${inputField.kana02}`));
await driver.findElement(By.xpath('//*[@id="form1"]/table/tbody/tr[3]/td')).getText()
.then(value => expect(value).toBe(`〒${inputField.zip01}-${inputField.zip02}`));
await driver.findElement(By.xpath('//*[@id="form1"]/table/tbody/tr[4]/td')).getText()
.then(value => expect(value).toContain(`${inputField.addr01}${inputField.addr02}`));
await driver.findElement(By.xpath('//*[@id="form1"]/table/tbody/tr[5]/td')).getText()
.then(value => expect(value).toBe(`${inputField.tel01}-${inputField.tel02}-${inputField.tel03}`));
await driver.findElement(By.xpath('//*[@id="form1"]/table/tbody/tr[6]/td')).getText()
.then(value => expect(value).toBe('[email protected]'));
await driver.findElement(By.xpath('//*[@id="form1"]/table/tbody/tr[7]/td')).getText()
.then(value => expect(value).toBe('お問い合わせ内容入力'));
} finally {
driver && await driver.quit();
}
let completeMessage: HttpMessage;
test('お問い合わせ内容を送信します', async () => {
await page.click('#send');
await expect(page.locator('h2.title')).toContainText('お問い合わせ(完了ページ)');
completeMessage = await zapClient.getLastMessage(url);
});

describe('[ATTACK] お問い合わせ(確認ページ)をスキャンする', () => {
test('POST でお問い合わせ(確認ページ)をスキャンする', async () => {
test.describe('テストを実行します[POST][入力→確認] @attack', () => {
let requestBody: string;
test('transactionid を取得し直します', async () => {
await page.goto(url);
const transactionid = await page.locator('input[name=transactionid]').first().inputValue();
requestBody = confirmMessage.requestBody.replace(/transactionid=[a-z0-9]+/, `transactionid=${transactionid}`);
});

test('アクティブスキャンを実行します', async () => {
expect(requestBody).toContain('mode=confirm');
const scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'POST', requestBody);
await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000, page);
});

test('結果を確認します', async () => {
await zapClient.getAlerts(url, 0, 1, Risk.High)
.then(alerts => expect(alerts).toEqual([]));
});
});

const message = await zapClient.getLastMessage(url);
const scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'GET', message.requestBody); // XXX なぜか method=POST にすると url_not_found のエラーになる. GET にしていても POST でスキャンされる
test.describe('テストを実行します[POST][確認→完了] @attack', () => {
let requestBody: string;
test('transactionid を取得し直します', async () => {
await page.goto(url);
const transactionid = await page.locator('input[name=transactionid]').first().inputValue();
requestBody = completeMessage.requestBody.replace(/transactionid=[a-z0-9]+/, `transactionid=${transactionid}`);
});

await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000);
test('アクティブスキャンを実行します', async () => {
expect(completeMessage.responseHeader).toContain('HTTP/1.1 302 Found');
expect(requestBody).toContain('mode=complete');
const scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'POST', requestBody);
await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000, page);
});

const alerts = await zapClient.getAlerts(url, 0, 1, Risk.High);
alerts.forEach(alert => {
throw new Error(alert.name);
});
expect(alerts).toHaveLength(0);
test('結果を確認します', async () => {
await zapClient.getAlerts(url, 0, 1, Risk.High)
.then(alerts => expect(alerts).toEqual([]));
});
});
});
Loading

0 comments on commit 8f73fdb

Please sign in to comment.