Процесс реализации и сборки веб приложения в рамках дисциплины "Программирование" с использованием методологии DevOps (CI/CD). Telegram motivation bot.

CI (Continuous Integration)

Непрерывная интеграция осуществляется по средства GitHub. На вкладке Actions, можно отследить весь процесс интегрирования. С помощью данного инструмента легко отследить весь процесс разработки, он позволяет организовать эффективную работу в команде.

image-20230114232419551

main.yml

В данном файле описан основной рабочий процесс, который поможет нам начать работу с Actions. В нем контролируется, когда процесс будет запущен по запросу push или pull для ветки "master"

name: CI

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the "master" branch
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  init:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [ 18.x ]

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      - uses: actions/checkout@v3
      - name: Staring Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - name: Install modules
        run: npm install

      - name: build
        run: npm ci

      - name: test
        run: ./mocha.sh

image-20230114234154406

Pull requests

image

Для добавления тестов была создана ветка CI, в который велась разработка тестов. В данной дирректории создатеся слепок ветки, далее проходит CI init проверка при push на ветку master. Затем предоставляется актуальная информация о работе кода, в случае если все хорошо происходит merge, при котором нововедения встраиваются в основной код. Для тестирования функциолнальной части приложения использовалась библеотеки mocha и chai.

Mocha – основной фреймворк. Он предоставляет общие функции тестирования, такие как describe и it, а также функцию запуска тестов. Chai – библиотека, предоставляющая множество функций проверки утверждений.

script.test.js

В данном файле содержаться автотесты, которые проходят при CI.

import { expect } from 'chai'
import { phrases } from '../phrases.js'
import { getRandomElement } from '../utilities.js'

describe('Тесты переменных', () => {
    it('Фразы в переменной phrases должны иметь тип string', () => {
        phrases.map((phrase) => {
            expect(typeof(phrase)).equals('string', `Переданное значение не является строкой`)
        })
    })

    it('getRandomElement возвращает случайный элемент массива (на примере phrases)', () => {
        expect(getRandomElement(phrases)).not.equals(getRandomElement(phrases), 'Сравниваемые значения равны')
    })

    it('getRandomElement возвращает элемент массива-аргумента', () => {
        expect(phrases).contains(getRandomElement(phrases), 'Переданное значение не является элементом массива')
    })
})

CD

Данный этап был реализован с помощью таких сервисов как Checklly и Versel.

Тестирование CD выполенены на @playwright/test на сервисе Checkly. Github отправляет информацию, на хостинг Versel, далее Versel обращается к Checkly, где записаны наши тесты, которые проверяют фронтенд составляющею: Отрисовку страницы и работоспособность кнопок. Затем Checkly датет ответ если тесты пройдены, и далее мы получаем финальный результат.

Versel

image

image

Checkly

image

image

@playwright/test
const { devices, test, expect } = require('@playwright/test')
test.describe('Page tests', () => {
  test.beforeEach(async ({page}) => {
    await page.goto(process.env.ENVIRONMENT_URL || 'https://tg-motivation-bot-front.vercel.app/')
  })

  function delay(ms) {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, ms);
    });
  }

  test('should all page`s elements be rendered', async ({ page }) => {
    const logo = page.locator('p.logo')
    const button = page.locator('div.button')
    const phrase = page.locator('span.phrase')
    await expect(phrase).toBeVisible()
    await expect(logo).toBeVisible()
    await expect(button).toBeVisible()

    await page.screenshot({ path: 'elementsRenderedScreenshot.jpg' })
  })

  test.describe('Tests of phrase-block', async () => {
    test('should phrase-block render 4 phrases', async ({page}) => {

      const phrase = page.locator('span.phrase')
      async function loadPhraseText(ms = 8000) {
        return await delay(ms).then(async () => await phrase.allInnerTexts())
      }
      const phraseText = (await phrase.allInnerTexts())[0]
      const newPhraseText = (await loadPhraseText(10000))[0]

      expect(newPhraseText).not.toBe(phraseText)

      await page.screenshot({ path: 'lastPhraseScreenshot.jpg' })
    })

    test('should render another quote after click on the button', async ({page}) => {

      const phrase = page.locator('span.phrase')
      const button = page.locator('div.button')
      async function loadPhraseText(ms = 8000) {
        return await delay(ms).then(async () => await phrase.allInnerTexts())
      }
      const phraseText = (await loadPhraseText(10000))[0]
      await button.click()

      expect((await loadPhraseText(2000))[0]).not.toBe(phraseText)

      await page.screenshot({ path: 'phraseAfterClickScreenshot.jpg' })
    })
  })
})
test.describe('Device site tests', () => {
  test('should mobile device be emulated', async ({ browser }) => {
    const iPhone = devices['iPhone SE']
    const page = await browser.newPage({
      ...iPhone,
    })

    await page.goto(process.env.ENVIRONMENT_URL || 'https://tg-motivation-bot-front.vercel.app/')

    await page.screenshot({ path: './emulationScreenshot.png' })
  })

  test('throttle the browser network', async ({ page }) => {
    const client = await page.context().newCDPSession(page)
    await client.send('Network.enable')
    await client.send('Network.emulateNetworkConditions', {
      offline: false,
      downloadThroughput: (4 * 1024 * 1024) / 8,
      uploadThroughput: (3 * 1024 * 1024) / 8,
      latency: 2000,
    })

    await page.goto(process.env.ENVIRONMENT_URL || 'https://tg-motivation-bot-front.vercel.app/')
    await page.screenshot({ path: 'afterThrottleScreenshot.jpg' })
  })
})

Результаты прохождения тестов:

img

img

img

img

img

Результат реализации и сборки веб приложения в Telegram:

img

img