Двухфакторная аутентификация
вход на сервисы вроде Gmail и Facebook, с помощью WebDriver это делать не стоит
геттеры / сеттеры
нечто нерелевантное / внешнее / постороннее
eslint-plugin-jest
может предупреждать, когда в тесте нет утверждения (assertion)// handlers.js
import { rest } from 'msw';
export const handlers = [
rest.post('/login', (req, res, ctx) => {
// Сохраняем в сессии статус аутентификации пользователя
sessionStorage.setItem('is-authenticated', 'true');
return res(
// Отвечаем кодом 200
ctx.status(200),
);
}),
// ...
];
// browser.js
import { setupWorker } from 'msw';
import { handlers } from './handlers.js';
// Создаем Service Worker с переданными обработчиками запросов
export const worker = setupWorker(...handlers);
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
if (process.env.NODE_ENV === 'development') {
const { worker } = require('./browser.js');
worker.start();
}
ReactDOM.render(<App />, document.getElementById('root'));
rest.post('/login', (req, res, ctx) => {
if (req.body.username === 'real-user') {
// возвращаем ответ
// только когда `username` имеет нужное значение
return;
}
const { authToken } = req.cookies;
if (isValidToken(authToken)) {
return res(
ctx.json({id: 'abc-123', firstName: 'John'}),
)
}
return res(
ctx.status(403),
ctx.json({ message: 'Failed to authenticate!' }),
)
});
rest.get('https://api.github.com/users/:username', async (req, res, ctx) => {
// Исходный запрос к URL, получаем ответ
const originalResponse = await ctx.fetch(req);
const originalResponseData = await originalResponse.json();
return res(
ctx.json({
location: originalResponseData.location,
firstName: 'Not the real first name',
}),
);
});
const handler = rest.get('/books', (req, res, ctx) => {
return res(ctx.json({ title: 'The Lord of the Rings' }));
});
const worker = setupWorker(handler);
worker.start({
onUnhandledRequest(req) {
console.error(
'Found an unhandled %s request to %s',
req.method,
req.url.href,
)
},
});
DRY
-принцип: создаем переиспользуемый код избегая повторовHTML
страницей или ее частьюМанипулируйте элементами страницы, не углубляясь в HTML
findElementWithClass('album')
=> selectAlbumWithTitle()
findElementWithClass('rating').setText(5)
=> updateRating(5)
class SearchPage {
constructor(page) {
this.page = page;
}
async navigate() {
await this.page.goto('https://mail.ru');
}
async search(text) {
await this.page.fill('[data-testid="search-input"]', text);
await this.page.press('[data-testid="search-button"]', 'Enter');
}
}
import SearchPage from './models/search.js';
test("search", () => {
const page = await browser.newPage();
const searchPage = new SearchPage(page);
await searchPage.navigate();
await searchPage.search('search query');
// ...
})
test("login", () => {
// заполняем данные на странице входа
driver.findElement(By.name("username")).sendKeys("testUser");
driver.findElement(By.name("password")).sendKeys("testPassword");
driver.findElement(By.name("sign-in")).click();
// проверяем, что появляется тег h1 с текстом "Hello testUser" после входа
driver.findElement(By.tagName("h1")).isDisplayed();
expect(driver.findElement(By.tagName("h1")).getText()).toBe("Hello testUser");
}
class SignInPage {
protected WebDriver driver;
// <input name="user_name" type="text" value="">
private usernameBy: By = By.name("username");
// <input name="password" type="password" value="">
private passwordBy: By = By.name("password");
// <input name="sign_in" type="submit" value="SignIn">
private signinBy: By = By.name("sign-in");
constructor(dirver) {
this.driver = driver;
}
loginValidUser(userName: string, password: string): HomePage {
driver.findElement(usernameBy).sendKeys(userName);
driver.findElement(passwordBy).sendKeys(password);
driver.findElement(signinBy).click();
return new HomePage(driver);
}
}
public class HomePage {
protected driver: WebDriver;
// <h1>Hello userName</h1>
private By messageBy = By.tagName("h1");
constructor(driver: WebDriver) {
this.driver = driver;
if (!driver.getTitle().equals("Home Page of logged in user")) {
throw new Error(`This is not Home Page of logged in user, current page is: ${driver.getCurrentUrl()}`);
}
}
getMessageText(): string {
return driver.findElement(messageBy).getText();
}
}
test("login", () => {
const signInPage: SignInPage = new SignInPage(driver);
const homePage: HomePage = signInPage.loginValidUser("userName", "password");
expect(homePage.getMessageText()).toBe("Hello userName"));
});
Try not to expose the internals of the page
Не создавайте объект для всей страницы, только значимые элементы
Общедоступные методы представляют услуги, предлагаемые на странице
Старайтесь не показывать внутренности страницы
Если поведение должно отличаться
class LoginPage {
public HomePage loginAs(username: string, password: string) {
// ... здесь логинимся
}
public LoginPage loginAsExpectingError(username: string, password: string) {
// ... здесь неудавшийся логин
}
public getErrorMessage(): string {
// здесь проверяем, верное ли сообщение об ошибке выбрасывается
}
}
Можно наследоваться
class LoginPage extends Page {
get username() {
return $('#username');
}
get password() {
return $('#password');
}
get submitBtn() {
return $('form button[type="submit"]');
}
get flash() {
return $('#flash');
}
get headerLinks() {
return $$('#header a');
}
open() {
super.open('login')
}
submit() {
this.submitBtn.click();
}
}
Вам ответят команда поддержки Хекслета или другие студенты.
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
Зарегистрируйтесь или войдите в свой аккаунт