Если вы уже знакомы с АЕМ-ом, смело пропускайте эту часть. Если же нет - вам стоит понять, что же такое этот АЕМ и почему с его тестированием возникают сложности.

AEM - content management system от Adobe (как выразилась коллега - WordPress на стероидах). Что это значит? Мы не создаем непосредственные веб страницы, а работаем над сложной админкой (AEM-author), которая в будущем позволит контенщикам (Editors) создавать эти страницы, используя набор определенных компонентов. Эти страницы будут видны (после publish действия) конечным юзерам (AEM-publish). Так что наша работа, собственно, и заключается в создании этих компонент. 

Стоит сделать еще одну ремарку. Под компонентой, стоит понимать не только user interface, но и его возможную конфигурацию, которая может участвовать в логике на беке. Например, компонентой будет карточка нашего продукта, при нажатии на которую будет выполнен процесс букинга продукта и переход на страницу оплаты оного. При чем, в это же время, будет отправлена информацию в Google аналитику и возможно, что-то еще.

И так, что мы имели — AEM version 6.4.4.0. Процессы тестирования, установленные задолго до нашего привлечения. Вся ответственность была возложена на автотесты:

  1. Screenshots tests — Поскольку AEM — content management system => Значит наши стили, да и вообще весь фронт очень важен, ведь это то с чем сталкивается конечный пользователь (возможно расскажу об этом в другой статье).

  2. Web-component tests — самые обычные UI тесты с использованием Cypress в качестве основы. Только проверялись не страницы, а компоненты.

  3. Web Performance tests — мониторинг производительности наших Web-страниц (с помощью Sitespeed)

Что мы строили — корпоративный сайт со всякими свистелками и перделками для отдела маркетинга для привлечения клиентов и увеличения продаж (с покупкой прям на сайте). Перед нами стояла задача разработать возможность едиторам создавать страницы с различными статьями. Создать компоненты, которые бы отображали созданные нами статьи в определенном порядке, плюс возможность применять фильтрацию, основываясь на подвязанных тегах.

Какая сложность возникает при автоматизации тестировании такого функционала? Зависит, где будет происходить фильтрация. В нашем случае всё происходило на стороне бека. Это осложняет тестирование, поскольку, создает невероятное количество комбинаций статей и соответствующих тегов. Они же, в свою очередь, должны где-то хранится. 

С одной стороны, все очевидно — фильтр подразумевает наличие критериев и соответствующих сущностей. А если добавить сюда возможность создания новых критериев и новых сущностей? Задача резко усложняется. Добавляем итеративную разработку — изменения происходят не только в страницах, но и в логике их работы. Всё. Теперь мы не можем полагаться только на статический контент.

Представим, что мы таки оставляем статический контент. Тогда подобные изменения требуют от нас менять существующий контент для соответствия новой имплементации. При 5 страницах в этом нет ничего сложного, вот только становиться затратно по времени, если у вас 10+ страниц.

И так какой же выход?

А что если мы будем создавать страницы на лету? 

Создали несколько страниц, пошли проверили нашу функциональность для данной комбинации, создали новые, проверили для них.

Long story short

К сожалению, АЕМ не предоставляет никакой информации о своем API. А если такая информация где-то и есть, я не смог ее найти. Да и тестирование АЕМ-а — минимально описанная головная боль (Тестируете АЕМ? — Делитесь в комментариях как). 

Так что последующие выводы целиком и полностью — reverse engineering построенный на запросах отправленных фронтом (AEM-author) на бек.

В моем случае, и я уверен — в подавляющем большинстве случаев, - создание страниц в АЕМ-е сведется к следующим действиям:

  1. Создание страницы с помощью темплейта (page template)

  2. Добавление компонент на страницу

  3. Конфигурация компонент

  4. Паблиш страницы

    И, конечно, мы будем:

  5. Удалять страницы


Приступим...

Нам понадобиться dev-tools на вкладке Network. 

1. Создание страницы с помощью темплейта

  1. Открываем AEM-author

  2. Sites

  3. Переходим в нужную нам папку

  4. Жмякаем Create

  5. Выбираем нужный нам template

  6. Заполняем интересующие нас поля

  7. Запускаем запись Network запросов в dev-tools

  8. Нажимаем Create

Первый же запрос `${aem-author-URL}/libs/wcm/core/content/sites/createpagewizard/_jcr_content` будет содержать всю необходимую нам информацию.

Тут мы сталкиваемся с первой сложностью. Это POST запрос с FormData. Т.е. запрос не содержит привычный многим тестировщикам body в виде JSON/XML объекта. Вместо этого данные отправляются как Content-Type: application/x-www-form-urlencoded.

Во многих случаях любой JS объект можно легко перевести в данный формат (по-сути, это будет простая url encoded строка key=value записанная через &). Правда, тут нужно быть осторожными, поскольку данный формат не подразумевает, что ключ (key) является уникальным. Т.е. ваша Form Data может содержать tags=Tag1&tags=Tag2 (несколько тегов у страницы, в моем случае).

[Request Example] URL encoded FormData
[Request Example] URL encoded FormData
[Request Example] Parsed FormData
[Request Example] Parsed FormData

Самое важное на что стоит обратить внимание на этих скриншотах:

  • parentPath — папка/страницв в AEM-e, в которой будет создана наша страница

  • template — путь к теплейту, который будет использован для создания страницы

… Ниже идут поля, применимые к моему проекту …

  • ./jcr:title — имя/тайтл моей страницы (обязательное поле на UI)

  • ./cq:tags — тег, который я добавил странице (опциональное поле)

  • ./articleDate, ./articleTimeToRead и :cq_csrf_token

Все остальные поля, опциональны и могут быть опущены в запросах.

Теперь, на счет авторизации. Как видно выше, запросы содержат token. Я использовал cypress.io для написания автотестов, так что для авторизации API запросов просто указывал auth объект с username и password, на ряду с методом, хедером и body. (For more info check Cypress: Request - arguments and http-authentication).

Реализация отправки POST запроса с FormData
Реализация отправки POST запроса с FormData

key takeaways: все запросы на создание новых страниц идут на `${aem-author-URL}/libs/wcm/core/content/sites/createpagewizard/_jcr_content`, parentPath и template присутствуют для всех вариантов создания страниц с помощью темплейта.


2. Добавление компонент на страницу

  1. Находим нужную нам страницу в AEM author

  2. Жмякаем Edit

  3. Выбираем нужную позицию (место) для компоненты 

  4. Жмякаем +

  5. Запускаем запись Network запросов в dev-tools

  6. Находим и выбираем нужный нам компонент

Нужный нам запрос `${aem-author-URL}/content/${page-path}/jcr:content/par/${some-url-part}/par/`.

[Request Example] Add Component to the page
[Request Example] Add Component to the page

Из важного тут:

  • ./@CopyFrom — темплейт (default) конфигурации компоненты (button в моем случае)

  • ./sling:resourceType — название и путь к компоненте, которую я добавлял

  • parentResourceType — тут я не уверен, судя по всему место, куда добавить компоненту


3. Конфигурация компонент

  1. На нужной нам странице выбираем необходимый компонент

  2. Жмякаем “гаечный ключ”

  3. Модифицируем конфигурацию данной компоненты

  4. Запускаем запись Network запросов в dev-tools

  5. Жмякаем кнопку Done

Наш запрос первый в списке `${aem-author-URL}/content/${page-path}/_jcr_content/par/${component-name}`.

[Request Example] Configure Component
[Request Example] Configure Component

Из важного тут:

  • ./sling:resourceType — название и путь к компоненте, которую я добавлял

  • :cq_csrf_token — токен, значит нужно использовать auth


4. Паблиш страницы

  1. Находим нужную нам страницу

  2. Запускаем запись Network запросов в dev-tools

  3. Жмякаем Quick Publish -> Publish

4.1.

В данном случае нам нужны первые 2 запроса.

4.1.1. Получение связанных ассетов

Запрос reference.json… — `${aem-author-URL}/libs/wcm/core/content/reference.json?${url-params}` — получаем информацию о ассетах (assets), cвязаных с нашей страницей.

[Request Example] Check Assets related to the published page
[Request Example] Check Assets related to the published page

Из важного — тут используются query string params. В path указан путь к нашей странице.

В ответе нам придет массив ассетов. Нам понадобятся path`s тех, чей published статус false.

[Responce Example] Check Assets
[Responce Example] Check Assets

4.1.2. Публикация страницы и ассетов

Запрос replicate — `${aem-author-URL}/bin/replicate` — запрос на публикацию нашей страницы и связанных с ней сущностей.

[Request Example] Publish Page and Related Assets
[Request Example] Publish Page and Related Assets

Как вы видите, основное на что стоит обратить внимание:

  • cmd: Activate — команда для паблиша страницы

  • path — пути к нашей странице и к 2м ассетам

4.2.

Поскольку паблишинг страниц — асинхронное действие. Необходимо убедиться, что он состоялся.

Для этого нам понадобиться еще один запрос — GET `${aem-author-URL}/etc/replication/agents.author/publish_publish/jcr:content.queue.json`. Он вернет нам массив страниц ожидающих паблишинга. Так что придётся сделать, как минимум еще один запрос, проверить есть ли необходимая нам страница в массиве body.queue. Опять же искать по path. Если страница все еще присутствует в очереди, придётся повторить проверку один или даже несколько раз (я выставил timeout в 1 секунду, но думаю можно и меньше).


5. Удаление страницы

  1. Находим нужную нам страницу

  2. Выбираем ее

  3. Запускаем запись Network запросов в dev-tools

  4. Жмякаем Delete-> Delete

Наш запрос `${aem-author-URL}/bin/wcmcommand`.

[Request Example] Delete Page
[Request Example] Delete Page

Из важного тут:

  • cmd — deletePage

  • path — путь к странице

  • force: false — но я бы рекомендовал ставить true (дабы при удалении не происходило дополнительных проверок)

  • checkChildren: true — можно опустить


Итак, подведем итог… 

Выше описанным способом можно создавать и конфигурировать страницы в АЕМ-е на лету. Основная проблема возникшая у меня — разобраться с последовательностью действий и структой отправляемых запросов. 

В подавляющем большинстве все они содержат FormData. И тут вам придётся выбрать способ, откуда брать эту информацию (использовать готовые моки или генерировать на лету). Я выбрал второй способ и с помощью билдеров создавал необходимые мне структуры FormData (но это отдельная история).

Для тех кто дочитал аж сюда, несколько странностей вылезших в процессе создания страниц таким способом:

  • AEM заменяет пробелы на `-`. То есть если вы создали страницу с Тайтлом `Bla 1 2 3 4` и не указали специфический путь к ней, тогда АЕМ сделает эту страницу доступной по пути …/bla-1-2-3-4

  • Пути страниц всегда будут в lowerCase (см пред. пример)

  • При использовании `_` в тайтле начиная с (приблизительно с 18и символов) АЕМ удалит все последующие `_`. Те если вы создали страницу с тайтлом BlaBla123456789123456_blabla, то доступ к ней будет не по …/blabla123456789123456_blabla, а по …/blabla123456789123456blabla

  • Для сетапа некоторых добавленных компонент понадобиться их id. Его можно получить в ответе запроса на добавление этой компоненты на страницу.