Как я могу утверждать, что элемент HTML не меняет своего внешнего вида после включения таблицы стилей? - PullRequest
2 голосов
/ 08 февраля 2020

Настройка

Я пишу плагин для сайта. Он собирается добавить в DOM элементы, стилизованные через плагин CSS. Я ожидаю, что стилизация будет ограничена плагином, то есть никакие элементы вне плагина не должны изменять свой внешний вид после того, как плагин включен на веб-странице.

Я запускаю интеграционные тесты с использованием Cypress. Как я могу утверждать, что стили всех существующих элементов остаются неизменными, когда плагин включен на страницу? У меня есть доступ к странице до и после загрузки плагина.

Подход

Я думаю, что это должно работать:

cy.visit('theURL');
getStyles().then(oldStyles => {                      // Get the styles of the elements
    mountPlugin();                                   // Mount the plugin (including CSS)
    getStyles().then(newStyles => {                  // Get the (possibly changed) styles
        newStyles.forEach((newStyle, i) =>           // Compare each element’s style after
            expect(newStyle).to.equal(oldStyles[i])  //+ mounting to the state before mounting
        );
    });
});
function getStyles() {
    return cy.get('.el-on-the-page *').then((elements) => { // Get all elements below a certain root
        const styles: CSSStyleDeclaration[] = []
        elements.each((_, el) => {                          // Get each element’s style
            styles.push(window.getComputedStyle(el));       //+ and put them it an array
        });
        return styles;                                      // Return the styles
    });
}

Проблемы

Числа c ключи в CSSStyleDeclaration

Строка expect(newStyle).to.equal(oldStyles[i]) завершается ошибкой, поскольку oldStyles[i] содержит цифры c ключи, которые содержат только имена свойств. Например,

// oldStyles[i] for some i
{
    cssText: "animation-delay: 0s; animation-direction: normal; […more]"
    length: 281
    parentRule: null
    cssFloat: "none"
    0: "animation-delay"   // <-- These elements only list property names, not values
...                        //+
    280: "line-break"      //+
    alignContent: "normal" // <-- Only here there are actual property values
...                        //+
    zoom: "1"              //+
...
}

Обходной путь

Я исправляю это, вручную обходя клавиши CSS и проверяя, является ли ключ числом. Однако эти цифры c отображаются только в oldStyles, а не в newStyles. Я пишу это, потому что это выглядит подозрительно для меня, и я предполагаю, что ошибка уже может быть там.

// Instead of newStyles.foreach(…) in the first snippet
newStyles.forEach((newStyle, i) => {
    for (const key in newStyle) {
        if(isNaN(Number(key))) {
            expect(newStyle[key]).to.equal(oldStyles[i][key]);
        }
    }
});

Пустые значения свойств

Я делаю неявное предположение, что здесь DOM фактически загружен и применяет стили. Насколько я понимаю, вызов getLinkListStyles на cy.get должен быть запланирован на выполнение только после того, как cy.visit дождался, пока окно не запустит событие load.

Из документации Cypress :

cy.visit() разрешается, когда на удаленной странице запускается событие load.

Однако, с использованием вышеупомянутого временного решения, я получаю пустую строку для CSS правила в oldStyles. Например:

//oldStyles[i] for some i
{
    cssText: "animation-delay: ; animation-direction: ; animation-duration: ; […more]"
    length: 0
    parentRule: null
    cssFloat: ""
    alignContent: ""
...
}

Попытки решения

Обратите внимание, что это поведение не меняется, когда я явно использую обратный вызов с cy.visit, то есть:

cy.visit(Cypress.env('theURL')).then(()=>{
    getStyles().then((oldStyles) => {
        // (rest as above)

Ни то, ни другое cy.wait(15000) в начале getStyles():

function getStyles() {
    cy.wait(15000); // The page has definitely loaded and applied all styles by now
    cy.get('.el-on-the-page *').then((elements) => {
...

1 Ответ

1 голос
/ 09 февраля 2020

Я не могу ответить на вопрос о пустых значениях свойств, обходной путь не должен влиять на вещи. Если я правильно понимаю, вы получаете значения свойств, когда не используете обходной путь?

Нумери c ключи

Это почти наверняка индексы в cssText стиль, который является встроенными стилями .

Количество цифр c точно такое же, как у записей в cssText, и значения соответствуют LHS пар ключ-значение в cssText.

Отсутствуют цифры c ключей на втором getStyles ()

Вы уверены?

Если я запускаю ваш код без монтирования плагина, я получаю ошибку, потому что он сравнивает ссылки на объекты,

getStyles().then(oldStyles => {
  // no plugin mounted
  getStyles().then(newStyles => {                
    newStyles.forEach((newStyle, i) =>           
      expect(newStyle).to.equal(oldStyles[i])
    );
 });

, но если я использую .to.deep.equal, это успешно

getStyles().then(oldStyles => {
  // no plugin mounted
  getStyles().then(newStyles => {                
    newStyles.forEach((newStyle, i) =>           
      expect(newStyle).to.deep.equal(oldStyles[i])
    );
 });

getComputedStyle () возвращает живой объект

MDN Window.getComputedStyle ()

Возвращенный стиль - это живой объект CSSStyleDeclaration, который обновляется автоматически при изменении стилей элемента.

так что вам нужно будет клонировать результат перед сравнением, даже если плагин изменил что-то, когда вы сравниваете, они будут идентичны.

Я бы предложил применить JSON/stringify() к результату и сравнить строки, это довольно быстро, также устраняет необходимость глубокого выравнивания.

function getStyles() {
  return cy.get('.el-on-the-page *').then((elements) => {
    const styles = []
    elements.each((_, el) => {
      styles.push(window.getComputedStyle(el));
    });
    return JSON.stringify(styles);    
  });
}

getStyles().then(oldStyles => {         
  mountPlugin();       
  getStyles().then(newStyles => {         
    expect(newStyles).to.equal(oldStyles);
  });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...