Jsdom: Поддержка API веб-компонентов

Созданный на 17 февр. 2015  ·  89Комментарии  ·  Источник: jsdom/jsdom

Мы пытаемся использовать jsdom для наших модульных тестов ядра React (http://facebook.github.io/react/).

К сожалению, спецификация веб-компонентов изначально не поддерживается jsdom, а полифилл webcomponents.js не работает на jsdom. Эта проблема требует добавления поддержки веб-компонентов (пользовательские элементы, теневой дом, импорт html и т. д.).

Самый полезный комментарий

TL;DR

Последние 2 недели я работал над оценкой возможности добавления поддержки пользовательского элемента в jsdom. Вот результат расследования.

Вы можете найти реализацию пользовательского элемента, соответствующую спецификации, здесь: https://github.com/jsdom/jsdom/compare/master...pmdartus :custom-elements?expand=1. Здесь и там все еще есть некоторые шероховатости, но, по крайней мере, большая часть теста WPT проходит . Оставшиеся неудачные тесты — это либо известные проблемы JSDOM, либо незначительные проблемы, которые можно решить, когда мы займемся фактической реализацией в jsdom.

А теперь хорошие новости: теперь, когда Shadow DOM поддерживается изначально, как с веткой custom-element, так и с наблюдателем мутаций, я смог загрузить и отобразить последнюю версию примера приложения Polymer 3 hello world в jsdom 🎉. В своем текущем состоянии ветвь не может загрузить приложение Stencil (режим разработки Stencil требует некоторых неподдерживаемых функций, таких как module , а режим prod срабатывает по неизвестной причине).

План действий

Вот список изменений, которые должны произойти в первую очередь, прежде чем приступить к фактической реализации спецификации пользовательского элемента. Каждый пункт в списке независим и может решаться параллельно.

Поддержка расширенных атрибутов IDL [CEReactions]

Одним из основных функциональных элементов, отсутствующих в jsdom для добавления поддержки пользовательских элементов, являются атрибуты [CEReactions] . Я частично смог обойти эту проблему, исправив нужные свойства прототипа. Этот подход работает до тех пор, пока стек реакций пользовательского элемента является глобальным, а не на единицу связанных контекстов просмотра аналогичного происхождения, поскольку все прототипы интерфейсов являются общими.

Этот подход имеет некоторые недостатки, поскольку некоторые интерфейсы имеют атрибуты [CEReactions] , связанные с индексированными свойствами ( HTMLOptionsCollection , DOMStringMap ). Внутри jsdom использует прокси для отслеживания изменения этих свойств. Прототип патчинга интерфейса в этом случае не работает. Другой подход, позволяющий обойти эту проблему, заключается в исправлении реализации вместо интерфейса (не реализованного).

Я недостаточно знаком с внутренним устройством webidl2js, но мы должны изучить возможность добавления глобального хука для [CEReactions] ?

Изменения:

Поддержка расширенных атрибутов IDL [HTMLConstructor]

Как объяснил @domenic выше , добавление поддержки [HTMLConstructor] здесь является одним из основных блокировщиков.

Я смог обойти эту проблему здесь, исправив конструктор интерфейса для каждого контекста просмотра. Конструктор интерфейса сможет получить доступ к нужному окну и объекту документа, сохраняя при этом общий прототип. Этот подход также позволяет избежать накладных расходов на переоценку прототипа интерфейса для каждого нового контекста просмотра.

Я не уверен, что это лучший подход, но он соответствует требованиям без дополнительных затрат на производительность.

Изменения:

Сделать спецификацию алгоритма синтаксического анализа фрагментов совместимой (#2522).

Как обсуждалось здесь , реализация алгоритма синтаксического анализа фрагментов HTML, используемая в Element.innerHTML и Element.outerHTML , неверна. Алгоритм синтаксического анализа необходимо подвергнуть рефакторингу, чтобы обратные вызовы реакций пользовательских элементов вызывались должным образом.

Изменения:

Улучшен поиск интерфейса для алгоритма создания элемента.

Одной из проблем, на которую я быстро наткнулся, было введение новых циклических зависимостей при добавлении поддержки создания пользовательских элементов. И CustomElementRegistry, и алгоритм создания элемента требуют доступа к интерфейсам Element, что создает кошмар циклических зависимостей.

Подход, принятый в ветке, заключался в создании InterfaceCache , который позволял бы искать интерфейс по пространству имен и имени элемента, а также по имени интерфейса. Модули интерфейса лениво оцениваются и кэшируются после оценки. Этот подход избавляет от циклических зависимостей, потому что интерфейсы больше не требуются на верхнем уровне.

Это один из подходов к решению этой давней проблемы в jsdom, одна из проблем с этим подходом заключается в том, что он может сломать веб-версию/браузерную версию jsdom (не тестировалось).

Изменения:

~Исправьте Element.isConnected для поддержки Shadow DOM (https://github.com/jsdom/jsdom/pull/2424)~

Это проблема, возникшая с введением теневого DOM , isConnected возвращает false , если элемент является частью теневого дерева. Здесь необходимо добавить новый WPT-тест, так как никакие тесты не проверяют это поведение.

Изменения:

Исправить документ узла HTMLTemplateElement.templateContents (#2426)

Содержимое шаблона , определенное в спецификации, имеет другой документ узла, чем сам HTMLTemplateElement. jsdom сегодня не реализует это поведение, а HTMLTemplateElement и содержимое его шаблона имеют одно и то же
узел документа.

Изменения:

  • HTMLTemplateElement-impl.js
  • htmltodom.js . Это изменение также оказывает некоторое влияние на синтаксический анализатор. Если элемент контекста является HTMLTemplateElement, алгоритм анализа фрагмента HTML должен извлекать узел документа из содержимого шаблона, а не из самого элемента.

Добавьте отсутствующие этапы внедрения в HTMLTemplateElement (#2426)

HTMLTemplateElement необходимо выполнить некоторые определенные шаги, когда он будет принят в другой документ. Насколько я знаю, это интерфейс почвы, чтобы иметь специальный шаг принятия. Реализация алгоритма узла принятия также должна быть обновлена, чтобы вызвать этот шаг принятия.

Изменения:

Добавлена ​​поддержка поиска isValue в сериализаторе parse5.

Алгоритм сериализации фрагментов HTML при сериализации элемента ищет значение is, связанное с элементом, и отражает его как атрибут в сериализованном содержимом. Было бы интересно добавить еще один хук в адаптер дерева parse5, который будет искать значение is, связанное с элементом getIsValue(element: Element): void | string .

Альтернативный подход (не реализованный) состоит в том, чтобы добавить изменение поведения текущего хука getAttrList , чтобы вернуть значение is в список атрибутов, если элемент имеет связанное значение is.

Представление

Перед любой оптимизацией производительности я также хотел проверить производительность изменений в ветке. Добавление пользовательских элементов увеличивает производительность на 10 % по сравнению с текущим результатом в эталонных тестах master for Tree Mutation. Однако создание новой среды JSDOM теперь в 3-6 раз медленнее по сравнению с основной, для выявления основной причины потребуется более глубокое исследование.

Подробнее: здесь

Все 89 Комментарий

Было бы интересно посмотреть, какие API использует webcomponents.js, которые jsdom не поддерживает. Если бы мне пришлось угадывать, это было бы намного проще реализовать, чем полную спецификацию веб-компонентов.

Тем не менее, было бы неплохо реализовать веб-компоненты. Вероятно, не так сложно, как можно было бы подумать — характеристики относительно невелики.

Просто было время немного вникнуть в это:

Во-первых, у нас нет Window , определенных в области окна. Я только что исправил это с помощью this.Window = this.prototype в конструкторе Window .
Во-вторых, webcomponentsjs ожидает, что у Window будет другой прототип, то есть прототип EventTarget , который мы не реализуем как отдельный объект.

Немного информации, потому что у меня было немного времени.

Хороший. Должна быть возможность довольно легко выставить окно. Прототип EventTarget немного сложнее, но кажется выполнимым, учитывая то, как мы сейчас реализуем этот материал; это было моим TODO.

Хорошо, патчи пока довольно просты:

  • [x] this.Window = Window; в конструкторе окна
  • [x] inherits(dom.EventTarget, Window, dom.EventTarget.prototype); после определения окна

Следующий сбой webcomponents.js происходит из-за того, что мы не реализовали HTMLUnknownElement (#1068), после того, как нам нужно было реализовать SVGUseElement . Это то, что я в настоящее время заблокирован, потому что webcomponents.js, по-видимому, не любит SVGUseElement , замаскированных HTMLDivElement , и выдает утверждение.

Хорошо, я еще немного проверил Polyfill, нам нужно реализовать / вам нужно проложить следующее:

  • [x] HTMLUnknownElement #1068
  • [ ] SVGUseElement
  • [ ] window.CanvasRenderingContext2D
  • [ ] Range API (в том числе: document.getRange() , window.getSelection() , window.Range , window.Selection ; #804 может быть началом)
  • [ ] npm i canvas

(список пока неполный)

Начало примерно такое:

jsdom.env({
  file: __dirname + '/index.htm', // refers to webcomponent.js
  created: function (err, window) {
    jsdom.getVirtualConsole(window).sendTo(console)

    window.document.createRange = function () { }
    window.getSelection = function () { }
    window.Range = function () { }
    window.Selection = function () { }
    window.CanvasRenderingContext2D = function () { } // Object.getPrototypeOf(require("canvas")(0,0).getContext("2d")) might be better
    window.SVGUseElement = window.HTMLUnknownElement
  },
  done: function (err, window) {
    console.log(err[0].data.error);
    console.log(window.CustomElements)
  },
  features: {
    ProcessExternalResources: ['script']
  }
});

После этого в нашем конструкторе HTMLDocument есть ошибка, которая приводит к максимальной ошибке стека вызовов. Конструктор в настоящее время предназначен только для внутреннего использования, однако допустимо, что какой-то скрипт на сайте вызывает его, поэтому нам нужно сделать этот конструктор доступным для общего пользования.

+1 Хотелось бы увидеть веб-компоненты на jsdom, особенно по мере роста популярности Polymer, было бы здорово иметь возможность тестировать пользовательские элементы в безголовой системе.

В настоящее время нет кросс-браузерного определения веб-компонентов, поэтому внедрять его преждевременно. (Мы не собираемся просто копировать Chrome.) А пока вы можете попробовать использовать Polymer с jsdom.

@domenic достаточно честно. Ну, это больше поддержка полифилла WebComponents.js, который мне нужен, так как это то, от чего зависит Polymer - или webcomponents-lite (полифиллирует все из них, за исключением Shadow DOM) на данный момент. Сделал несколько попыток заставить Polymer работать с jsdom, но пока безуспешно — я предполагаю, что задачи @Sebmaster в комментарии выше, по крайней мере, должны быть сначала исправлены.

Насколько я понимаю, речь идет о трех отдельных полифиллах. Тот, что в OP, отделен от Polymer. Кроме того, есть полифиллы webcomponents.org, которые раньше использовались в old-Polymer. Затем в Polymer 1.0 у них есть свои собственные полифилы, я думаю, которые на самом деле не полифилы, а вместо этого альтернативные библиотеки, которые делают что-то вроде веб-компонентов. Возможно, это облегченные веб-компоненты.

В репозитории WebComponentsJS говорится, что webcomponentsjs-lite — это вариант , предоставляющий полифиллы для всех _кроме_ Shadow DOM, которые Polymer затем независимо пытается шиммировать, используя свою систему Shady DOM. Поэтому я почти уверен, что Polymer полагается на веб-компоненты настолько, насколько это возможно, а полифилл WebComponentsJS выполняет черновую работу. Облегченная версия должна иметь значительно меньший вес (как ни странно ..), поэтому я посмотрю, смогу ли я точно определить, что именно нужно jsdom для облегченной версии. Как вы думаете, каковы шансы на то, что полифилл (облегченный или полный) будет работать в jsdom?

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

Да, я думаю, что мой список задач все еще применим и необходим для использования прокладок. Слияние #1227 может значительно ускорить реализацию интерфейсов, соответствующих стандартам, чтобы мы могли быстрее исправлять отсутствующие.

Я (вероятно, наивно) начал работать над добавлением CustomElementsRegistry, чтобы понять, как устроен jsdom. Я добавил «custom-elements/custom-elements-registry/define.html» в список тестов веб-платформы, и он проходит, когда не должен (я еще почти не реализовал). Я почти уверен, что тест на самом деле не выполняется, поскольку даже добавление throw в начало теста не помешает его прохождению. Так что я явно что-то упустил; помимо добавления теста в test/web-platform-tests/index.js , что еще мне нужно сделать?

Похоже, это вызвано ошибкой в ​​начальной строке const testWindow = iframe.contentDocument.defaultView; , потому что contentDocument по какой-то причине не определено. Возможно, проблема в нашем порядке загрузки и выполнении скрипта, но я не вникал в это. Надеюсь, это поможет вам обойти это. Возможно, нам придется упростить тест для наших целей (пока).

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

@Sebmaster На всякий случай, если вам интересно, я провел небольшое исследование того, что происходит с этим тестом, и результаты меня удивили.

В тесте используется функция именованного доступа HTML . Это означает, что вы можете делать такие вещи, как:

<div id="foo"></div>
<script>
  console.log(window.foo === document.getElementById('foo'));
</script>

_Однако_, если элемент имеет вложенный контекст просмотра, вместо этого глобальный элемент должен указывать на него (см. связанную спецификацию). Для iframe это contentWindow . jsdom понимает это правильно, есть даже тест . Сафари тоже справляется.

Что безумно, так это то, что Chrome и Firefox ошибаются; global указывает на iframe, а не на contentWindow. Увидев это, я предположил, что это ошибка jsdom, и немного поискал, в конце концов найдя этот тест, который привел меня к спецификации.

тлдр; работа над jsdom очень познавательна, и вы, ребята, делаете потрясающую работу.

Собираемся файлировать ошибки в соответствующих браузерах. Также отправлю PR на тесты веб-платформы, я нашел и другие ошибки в тесте.

Это еще больше мотивирует для восходящих тестов, таких как https://github.com/tmpvar/jsdom/blob/master/test/living-html/named-properties-window.js , для WPT. Спасибо за публикацию! Мне очень нравится jsdom ^_^

Привет!

Мне удалось заставить полифилл Custom Elements работать с jsdom, объединив

Примечание: репозиторий использует jsdom 8.5.0. Причина в том, что я добился успеха только с полифиллом MutationObserver, который использует Mutation Events внутри. События мутации были удалены после 8.5.0 из-за плохой производительности. Если выйдет нативный Mutation Observer, я удалю полифилл и обновлю его до последней версии jsdom.

У меня есть последняя версия jsdom, и https://github.com/WebReflection/document-register-element у меня работает! Я экспериментировал с более официальными полифиллами, и по какой-то причине у меня возникли проблемы. Моя цель - заставить работать хотя бы пользовательские элементы и импорт html... было бы здорово, если бы мы могли заставить работать и Polymer.

Я могу заставить скрипты Polymer работать без ошибок. Я даже могу создать компонент и передать его конструктору Polymer. После этого он молча терпит неудачу. Я думаю, что проблема с теневым DOM.

Я пытался заставить полифилл импорта HTML webcomponentsjs работать. Я могу запустить скрипт, и я считаю, что мой импорт HTML выполняет запрос xmlhttprequest, но не похоже, чтобы сценарии в моем импорте запускались.

Хотите поделиться примером @lastmjs? В настоящее время я сам по колено в веб-компонентах. Если я могу быть полезен, я с удовольствием внесу свой вклад вместе с вами.

@snuggs Спасибо! Дайте мне день или два, я сейчас занят кое-какими неотложными делами.

@snuggs Если мы сможем заставить полифил webcomponents-lite работать, мы сможем использовать Polymer. Shadow DOM кажется самым сложным полифилом для работы на данный момент, и если мы будем использовать webcomponents-lite , нам пока не придется об этом беспокоиться, потому что у нас будет доступ к template , custom elements и HTML imports .

Я могу заставить импорт HTML работать с полифиллом webcomponents-lite . Я столкнулся с каким-то странным поведением, затем я наткнулся на это: https://github.com/Polymer/polymer/issues/1535 Похоже, что импорт HTML может быть загружен только по нефайловому протоколу с поддержкой cors. Поэтому я развернул быстрый http-сервер в корневом каталоге моего проекта:

npm install -g http-server
http-server --cors

И вот основной код, с которым я работал:

const jsdom = require('jsdom');

const doc = jsdom.jsdom(`
    <!DOCTYPE html>

    <html>
        <head>
            <script src="bower_components/webcomponentsjs/webcomponents-lite.js"></script>
            <link rel="import" href="http://localhost:8080/bower_components/polymer/polymer.html">
        </head>

        <body>
            <test-app></test-app>

            <dom-module id="test-app">
                <template>
                </template>

                <script>
                    setTimeout(() => {
                        class TestApp {
                            beforeRegister() {
                                this.is = 'test-app';
                                console.log('before register');
                            }

                            ready() {
                                console.log('ready');
                            }

                            created() {
                                console.log('created');
                            }

                            attached() {
                                console.log('attached');
                            }
                        }

                        Polymer(TestApp);
                    }, 1000);
                </script>
            </dom-module>
        </body>
    </html>
`, {
    virtualConsole: jsdom.createVirtualConsole().sendTo(console)
});

По какой-то причине мне нужно обернуть экземпляр TestApp в setTimeout . Похоже, что импорт Polymer HTML не блокирует рендеринг остальной части HTML, поэтому без setTimeout конструктор Polymer не определен. Это нормальное поведение для импорта HTML?

beforeRegister вызывается, поэтому конструктор Polymer что-то делает. Итак, теперь у нас есть импорт HTML, конечно же, шаблоны, работающие с полифиллом webcomponents-lite . Я не уверен, как обстоят дела с полифиллом пользовательских элементов.

Когда я помещаю внутрь класса TestApp #$ метод ready или created , они не вызываются. Похоже, что события жизненного цикла не обрабатываются должным образом. Корень этой проблемы может быть в реализации полифилла пользовательских элементов. Я буду продолжать играть.

Возможные проблемы, которые необходимо решить:

  • [ ] Импорт HTML не блокируется должным образом
  • [ ] полифилл пользовательского элемента работает или нет?
  • [ ] Методы жизненного цикла полимера не вызываются

Больше возни приводит к большему пониманию. Я думаю, что порядок импорта и регистрации может нам все испортить. Когда я запускаю const testApp = document.createElement('test-app'); после вызова конструктора Polymer, вызываются методы created и ready , но не метод attached . Возможно, jsdom неправильно обрабатывает литералы пользовательских элементов? Кроме того, даже при вызове document.body.appendChild(testApp) метод жизненного цикла attached никогда не вызывается.

Это может помочь понять порядок загрузки: https://github.com/webcomponents/webcomponentsjs#helper -utilities

@lastmjs В настоящее время я переворачиваю монеты между CustomElementRegistry.define() и document.registerElement() . Я видел, что Доменик внес большой вклад и объединил некоторые работы, связанные с whatwg (https://github.com/whatwg/html/issues/1329). Похоже, происходит миграция API. Например, я полагаю, что спецификация вызывает connectedCallback , что сочетается с функциональностью attachedCallback . Также предполагается, что вы имели в виду attachedCallback , когда говорили attached , поскольку этот обработчик не является частью API. Я испытал define() и registerElement() , запуская разные обратные вызовы, соответствующие каждому методу. Я понял стратегию пользовательских элементов. HTMLImports Доменик упоминал перед реализацией, использующей исправление XMLHTTPRequest. Я считаю, что можно преобразовать непосредственно в DocumentFragment прямо из ответа. Пахнет так, может быть, змеиным маслом с "импортом". «Фальшивый» импорт может быть тем местом, где живет здравомыслие.

Также, похоже, происходит некоторое мошенничество с вызовом super() на HTMLElement при транспиляции из ES6 -> ES5, так что следите за этим. Я столкнулся с этим с Rollup.js/Babel и был вынужден использовать (облегченную) прокладку из пакета веб-компонентов.
https://developers.google.com/web/fundamentals/getting-started/primers/customelements

Наконец, кажется, я получаю (больше) успеха, когда создаю тег прототипа.

document.createElement('main', 'test-app')

Как @domenic упоминал мне ранее, мы хотим быть осторожными, чтобы реализовать спецификации с наименьшим общим знаменателем, а не просто делать то, что делает GOOGLE. Кажется, что линии размыты с веб-компонентами. Но я фанат.

Какими методами вы работали?

До сих пор я в основном играл только с полифиллами webcomponents-lite , а Polymer < 2.0. Поэтому, когда я упомянул метод attached , я имел в виду метод жизненного цикла Polymer, который они используют вместо attachedCallback . Кроме того, насколько мне известно, полифилы еще не перешли на новую спецификацию пользовательских элементов v1. Так что все, с чем я играю, только в надежде заставить Polymer работать с текущими полифиллами.

@snuggs Используете ли вы полифиллы прямо сейчас или работаете над реальной реализацией в jsdom?

@lastmjs Я не использую полифиллы, так как считаю, что нет необходимости выполнять 80% пути. Платформа уже достаточно зрелая, чтобы с небольшой предварительной настройкой можно было просто использовать нативные конструкции. Мне нравится использовать легкие (обычно ручные) инструменты вместо фреймворков. Тем не менее, это не большинство людей. Похоже, намерение Доменика — импортировать пользовательские элементы 👍 html 👎, но нет проблем с расширением XMLHTTPRequest для обработки fetching документа, что приведет нас к этому. Это было около 6 месяцев назад. Многое изменилось с тех пор в реализации. Вполне возможно, думает. Итак, где мы заканчиваем @lastmjs?

@snuggs Возможно, самое разумное и перспективное решение — реализовать первоклассную поддержку пользовательских элементов и теневого DOM в jsdom. Оба стандарта находятся на уровне v1, и, судя по тому, что я слышал, большинство основных браузеров будут их реализовывать. Как мы должны это сделать? Я сейчас ограничен во времени, но, возможно, мы сможем хотя бы выложить то, что нужно сделать. @domenic Есть ли у вас какие-либо предложения о том, как продвигаться вперед с этими реализациями, или какие-либо причины, по которым мы не должны этого делать?

Никаких конкретных предложений от меня, кроме реализации спецификации :)

У меня есть ветка, где я работал над этим некоторое время назад (с тех пор спецификация немного изменилась). Внедрение CustomElementsRegistry было достаточно простым, и я изо всех сил пытался понять, как вплести реакции пользовательских элементов в кодовую базу и когда их следует вызывать и откуда. Если бы я взял эту резервную копию (никаких обещаний), это, вероятно, то, на чем я бы сосредоточился.

@matthewp Звучит полезно, где я могу найти эту ветку?

@matthewp да, это было бы неплохо

https://github.com/matthewp/jsdom/commits/custom-elements , как я уже сказал, с тех пор спецификация изменилась, поэтому она устарела. И это самая простая часть, но это отправная точка, если кто-то хочет. @snuggs @lastmjs

(http://jonrimmer.github.io/are-we-componentized-yet/)

Лично просто поддерживать пользовательский элемент было бы уже здорово.

(Обратите внимание, что я понимаю, что phantomJS 2.5 должен поддерживать как минимум шаблоны и, возможно, пользовательский элемент, поскольку они переходят на более новую версию Webkit, не знаю, какую именно).

На самом деле, я издеваюсь над customElements, используя элемент lib document-register-element

const {before} = require('mocha')

before(mockDOM)
before(mockCustomElements)

function mockDOM() {
  const {JSDOM: Dom} = require('jsdom')
  const dom = new Dom('<!doctype html><html><body></body></html>')
  global.document = dom.window.document
  global.window = document.defaultView
  window.Object = Object
  window.Math = Math
}

function mockCustomElements() {
  require('document-register-element/pony')(window)
}

Круто, у тебя были проблемы?

пока нет :D

но мне нужно написать больше спецификаций, охватить больше вещей, чтобы чувствовать себя лучше

Удивительно видеть, что есть способ. Как бы мне ни нравился полимер, тестовая установка — это ад, а наличие jsdom в качестве запасного варианта — это хорошо ;) Спасибо за работу.

Похоже, пиар продвигает это вперед! https://github.com/tmpvar/jsdom/pull/1872

На самом деле, я издеваюсь над customElements, используя элемент lib document-register-element @darlanmendonca

Следует прочитать эту ссылку о присоединении глобалов jsdom к глобальному узлу. Это антипаттерн.

Всем привет,
Я немного запутался в статусе запуска Polymer внутри JSDOM (с использованием Node.js 6.7.0 и JSDOM 11.1.0). Я пробовал разные вещи, со смешанными результатами. Я был бы очень признателен, если бы кто-нибудь мог заполнить меня здесь...

Что я сделал до сих пор:

1) Я запустил http-сервер из своего корневого каталога

./node_modules/http-server/bin/http-server --cors

2) Я загрузил один из моих компонентов Polymer в JSDOM:

jsdom.JSDOM.fromURL("http://localhost:8080/path/to/my-component.html",
  { runScripts: "dangerously",
    resources: "usable"
  })
.then(function (dom) {
  setTimeout(() => {
    window = dom.window;
    component = window.document.querySelector("my-component");
  }, 10000);
})

(Я также пытался загрузить файл компонента из файловой системы с теми же результатами.)

3) Это мой код компонента:

<!DOCTYPE html>
<html>
<head>
  <script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
</head>

<body>
<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="order-app">
  <template>
    <h1>Hello Polymer</h1>
  </template>

  <script>
    console.log("javascript is being executed");
    addEventListener('WebComponentsReady', function () {
      console.log("web components are ready");
      Polymer({
        is: 'order-app'
      });
    });
  </script>
</dom-module>
</body>
</html>

(Я добавил заголовок HTML, чтобы загрузить полифилл веб-компонентов.)

Что я могу наблюдать?

Когда я запускаю это, я вижу

  • что полифилл веб-компонентов загружается с веб-сервера
  • сообщение "выполняется javascript" в консоли

Чего я не вижу

  • что компонент полимера.html загружается с веб-сервера
  • сообщение "веб-компоненты готовы" в консоли

Это приводит меня к выводу, что событие WebComponentsReady не запускается (вероятно, потому, что импорт HTML не работает?). Также,
window.WebComponents содержит { flags: { log: {} } } — индикатор ready отсутствует.

Я также пробовал насмешки и полифиллы:

  window.Object = Object;
  window.Math = Math;
  require('document-register-element/pony')(window);

но это, похоже, ничего не изменило.

Теперь мне интересно :-) Это вообще должно работать? Если да, то почему у меня не работает? Если нет, то чего не хватает/необходимо, чтобы заставить его работать?

Спасибо за любые идеи!

Мойн

Я даже пробовал это с еще меньшим успехом, и я не стал ждать, что будет результатом этого обсуждения здесь.

https://github.com/sebs/noframework/blob/master/test/configurator.js

не решение просто еще одна неудачная попытка. Та же путаница, кстати. Тоже такой же вывод

Полифиллинг пользовательских элементов в jsdom оказался очень сложной задачей. Может ли кто-нибудь перечислить проблемы, связанные с реализацией этого в jsdom? Попытка оценить уровень усилий, чтобы получить это.

Основное препятствие состоит в том, что jsdom разделяет конструкторы и их прототипы .

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

Я не уверен, есть ли хорошие промежуточные решения. Большая пушка состоит в том, чтобы перевести jsdom на архитектуру, которая допускает неразделяемые конструкторы/прототипы. Мы могли бы сделать это несколькими способами, каждый с разными компромиссами. Возможно, мы хотели бы открыть специальную тему для обсуждения с командой и сообществом, но сейчас позвольте мне перечислить те, которые приходят мне на ум:

  • Используйте [WebIDL2JSFactory] для всего в jsdom или, по крайней мере, HTMLElement и всех его потомков. Я не уверен, что [WebIDL2JSFactory] вообще хорошо работает с наследованием, но его можно заставить работать. Эта альтернатива заставляет всех платить за дополнительные конструкторы/прототипы, но, возможно, это лучше, чем делать пользовательские элементы опцией.
  • Есть вариант, когда jsdom запускает все модули определения класса внутри песочницы vm . Например, есть некоторый шаг сборки, который объединяет все веб-API в jsdom, а затем, когда вы создаете новое окно, мы делаем vm.runScript() с этим пакетом внутри новой глобальной песочницы. Это, вероятно, позволит нам избавиться от [WebIDL2JSFactory] .

Я предполагаю, что другим решением было бы реализовать пользовательские элементы с гигантским предупреждением о том, что реестр пользовательских элементов является глобальным для процесса Node.js? Хотя это кажется ужасным.


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

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

Я не уверен, что [WebIDL2JSFactory] вообще хорошо работает с наследованием, но его можно заставить работать.

Нет, это не так, и я не уверен, как именно заставить его работать. Второе решение, на мой взгляд, намного проще.

Есть вариант, когда jsdom запускает все модули определения класса внутри песочницы vm .

Это то, что я бы предпочел. Основная проблема заключается в передаче классов реализации в песочницу vm во время инициализации, хотя это можно сделать, поместив все из внешнего контекста в одно глобальное свойство, а затем delete это глобальное свойство. . Это также позволило бы правильно реализовать [NamedConstructor] и пару других расширенных атрибутов и, возможно, даже создать моментальный снимок запуска V8 для среды jsdom, если кто-то достаточно смел.

Вся эта история с [WebIDL2JSFactory] изначально была хакерской, и я хотел бы избавиться от нее как можно скорее.

Комментарии +1 бесполезны для разработки этой функции, поэтому я удаляю по крайней мере один недавний комментарий.

Привет, я не знал, что @TimothyGu работает над этим.
На самом деле у меня есть регистрация и создание пользовательских элементов, работающих в
https://github.com/mraerino/jsdom/tree/custom-elements-spec

Я стараюсь быть как можно менее инвазивным, а также оставаться как можно ближе к спецификации.
Проходят тесты веб-платформы Custom Element Registry.

Во время взлома прошлой ночью я нашел решение, которое работает без изменения webIdl2JS.
Смотрите здесь: https://github.com/mraerino/jsdom/commit/592ad1236e9ca8f63f789d48e1887003305bc618

@TimothyGu , не могли бы вы объединить усилия в этом?

Только некоторые обновления здесь:
Я вполне уверен в своей реализации спецификации, но в настоящее время застрял из-за расширенного атрибута IDL [HTMLConstructor] . Вот почему я открыл https://github.com/jsdom/webidl2js/issues/87 .

Тем временем я реализую алгоритм [HTMLConstructor] с использованием атрибута [Constructor] , чтобы можно было легко переключаться позже. (Сначала я реализовал это, вставив фиктивный класс HTMLElement в window , но это показалось мне неправильным.)

Да, как отмечено в https://github.com/tmpvar/jsdom/issues/1030#issuecomment -333994158, правильная реализация HTMLConstructor потребует фундаментальных изменений в архитектуре jsdom.

Есть ли у вас информация о том, сколько тестов веб-платформы проходит ваша версия?

Пока только customElementRegistry, и я могу ошибаться в своем прогрессе.

Редактировать: Хорошо, перечитав ваш комментарий, я понял, что вы имеете в виду. Я попробую это со своей реализацией, но @TimothyGu , похоже, тоже работает над разделением.

Я использую Polymer, поэтому я :+1: по этой функции запроса

@dman777 @mraerino То же самое для разработчиков slim.js. Slim использует собственный API веб-компонентов и не может наследовать HTMLElement без хаков на jsdom.

С момента открытия этого выпуска прошло три года. Кто-нибудь может сказать, когда примерно jsdom будет поддерживать пользовательские элементы?

TL;DR

Последние 2 недели я работал над оценкой возможности добавления поддержки пользовательского элемента в jsdom. Вот результат расследования.

Вы можете найти реализацию пользовательского элемента, соответствующую спецификации, здесь: https://github.com/jsdom/jsdom/compare/master...pmdartus :custom-elements?expand=1. Здесь и там все еще есть некоторые шероховатости, но, по крайней мере, большая часть теста WPT проходит . Оставшиеся неудачные тесты — это либо известные проблемы JSDOM, либо незначительные проблемы, которые можно решить, когда мы займемся фактической реализацией в jsdom.

А теперь хорошие новости: теперь, когда Shadow DOM поддерживается изначально, как с веткой custom-element, так и с наблюдателем мутаций, я смог загрузить и отобразить последнюю версию примера приложения Polymer 3 hello world в jsdom 🎉. В своем текущем состоянии ветвь не может загрузить приложение Stencil (режим разработки Stencil требует некоторых неподдерживаемых функций, таких как module , а режим prod срабатывает по неизвестной причине).

План действий

Вот список изменений, которые должны произойти в первую очередь, прежде чем приступить к фактической реализации спецификации пользовательского элемента. Каждый пункт в списке независим и может решаться параллельно.

Поддержка расширенных атрибутов IDL [CEReactions]

Одним из основных функциональных элементов, отсутствующих в jsdom для добавления поддержки пользовательских элементов, являются атрибуты [CEReactions] . Я частично смог обойти эту проблему, исправив нужные свойства прототипа. Этот подход работает до тех пор, пока стек реакций пользовательского элемента является глобальным, а не на единицу связанных контекстов просмотра аналогичного происхождения, поскольку все прототипы интерфейсов являются общими.

Этот подход имеет некоторые недостатки, поскольку некоторые интерфейсы имеют атрибуты [CEReactions] , связанные с индексированными свойствами ( HTMLOptionsCollection , DOMStringMap ). Внутри jsdom использует прокси для отслеживания изменения этих свойств. Прототип патчинга интерфейса в этом случае не работает. Другой подход, позволяющий обойти эту проблему, заключается в исправлении реализации вместо интерфейса (не реализованного).

Я недостаточно знаком с внутренним устройством webidl2js, но мы должны изучить возможность добавления глобального хука для [CEReactions] ?

Изменения:

Поддержка расширенных атрибутов IDL [HTMLConstructor]

Как объяснил @domenic выше , добавление поддержки [HTMLConstructor] здесь является одним из основных блокировщиков.

Я смог обойти эту проблему здесь, исправив конструктор интерфейса для каждого контекста просмотра. Конструктор интерфейса сможет получить доступ к нужному окну и объекту документа, сохраняя при этом общий прототип. Этот подход также позволяет избежать накладных расходов на переоценку прототипа интерфейса для каждого нового контекста просмотра.

Я не уверен, что это лучший подход, но он соответствует требованиям без дополнительных затрат на производительность.

Изменения:

Сделать спецификацию алгоритма синтаксического анализа фрагментов совместимой (#2522).

Как обсуждалось здесь , реализация алгоритма синтаксического анализа фрагментов HTML, используемая в Element.innerHTML и Element.outerHTML , неверна. Алгоритм синтаксического анализа необходимо подвергнуть рефакторингу, чтобы обратные вызовы реакций пользовательских элементов вызывались должным образом.

Изменения:

Улучшен поиск интерфейса для алгоритма создания элемента.

Одной из проблем, на которую я быстро наткнулся, было введение новых циклических зависимостей при добавлении поддержки создания пользовательских элементов. И CustomElementRegistry, и алгоритм создания элемента требуют доступа к интерфейсам Element, что создает кошмар циклических зависимостей.

Подход, принятый в ветке, заключался в создании InterfaceCache , который позволял бы искать интерфейс по пространству имен и имени элемента, а также по имени интерфейса. Модули интерфейса лениво оцениваются и кэшируются после оценки. Этот подход избавляет от циклических зависимостей, потому что интерфейсы больше не требуются на верхнем уровне.

Это один из подходов к решению этой давней проблемы в jsdom, одна из проблем с этим подходом заключается в том, что он может сломать веб-версию/браузерную версию jsdom (не тестировалось).

Изменения:

~Исправьте Element.isConnected для поддержки Shadow DOM (https://github.com/jsdom/jsdom/pull/2424)~

Это проблема, возникшая с введением теневого DOM , isConnected возвращает false , если элемент является частью теневого дерева. Здесь необходимо добавить новый WPT-тест, так как никакие тесты не проверяют это поведение.

Изменения:

Исправить документ узла HTMLTemplateElement.templateContents (#2426)

Содержимое шаблона , определенное в спецификации, имеет другой документ узла, чем сам HTMLTemplateElement. jsdom сегодня не реализует это поведение, а HTMLTemplateElement и содержимое его шаблона имеют одно и то же
узел документа.

Изменения:

  • HTMLTemplateElement-impl.js
  • htmltodom.js . Это изменение также оказывает некоторое влияние на синтаксический анализатор. Если элемент контекста является HTMLTemplateElement, алгоритм анализа фрагмента HTML должен извлекать узел документа из содержимого шаблона, а не из самого элемента.

Добавьте отсутствующие этапы внедрения в HTMLTemplateElement (#2426)

HTMLTemplateElement необходимо выполнить некоторые определенные шаги, когда он будет принят в другой документ. Насколько я знаю, это интерфейс почвы, чтобы иметь специальный шаг принятия. Реализация алгоритма узла принятия также должна быть обновлена, чтобы вызвать этот шаг принятия.

Изменения:

Добавлена ​​поддержка поиска isValue в сериализаторе parse5.

Алгоритм сериализации фрагментов HTML при сериализации элемента ищет значение is, связанное с элементом, и отражает его как атрибут в сериализованном содержимом. Было бы интересно добавить еще один хук в адаптер дерева parse5, который будет искать значение is, связанное с элементом getIsValue(element: Element): void | string .

Альтернативный подход (не реализованный) состоит в том, чтобы добавить изменение поведения текущего хука getAttrList , чтобы вернуть значение is в список атрибутов, если элемент имеет связанное значение is.

Представление

Перед любой оптимизацией производительности я также хотел проверить производительность изменений в ветке. Добавление пользовательских элементов увеличивает производительность на 10 % по сравнению с текущим результатом в эталонных тестах master for Tree Mutation. Однако создание новой среды JSDOM теперь в 3-6 раз медленнее по сравнению с основной, для выявления основной причины потребуется более глубокое исследование.

Подробнее: здесь

@pmdartus это очень перспективно, отличная работа! Я использовал свою ветку взлома jsdom-wc из-за отсутствия лучшего варианта. Я вижу какое-то странное поведение и надеялся поменяться на вашу ветку, но у меня возникают проблемы.

Я регистрирую пользовательские элементы, такие как:

class Component extends HTMLElement {

}

customElements.define('custom-component', Component);

Но если я сделаю:

const el = assign(this.fixture, {
  innerHTML: `
    <custom-component></custom-component>
  `,
});

Я получаю немедленное: Error: Uncaught [TypeError: Illegal constructor] .

Есть мысли по этому поводу?

Следующий фрагмент кода правильно работает в ветке custom-elements моего форка: https://github.com/pmdartus/jsdom/tree/custom-elements

const { JSDOM } = require("jsdom");

const dom = new JSDOM(`
<body>
  <div id="container"></div>
  <script>
    class Component extends HTMLElement {
        connectedCallback() {
            this.attachShadow({ mode: "open" });
            this.shadowRoot.innerHTML = "<p>Hello world</p>";
        }
    }
    customElements.define('custom-component', Component);

    const container = document.querySelector("#container");

    Object.assign(container, {
        innerHTML: "<custom-component></custom-component>"
    })

    console.log(container.innerHTML); // <custom-component></custom-component>
    console.log(container.firstChild.shadowRoot.innerHTML); // <p>Hello world</p>
  </script>
</body>
`, { 
    runScripts: "dangerously" 
});

Недопустимый конструктор, вероятно, вызван исходным конструктором HTMLElement, изменения, внесенные в ветку, должны исправлять конструктор для каждого нового объекта окна. @tbranyen У вас есть полный пример воспроизведения, чтобы я мог попробовать его локально?

Привет @pmdartus. Я еще не совсем уверен, что вызывает мои проблемы, но я написал некоторый изолированный код прямо в вашу ветку, которая отлично сработала:

const { JSDOM } = require('.');
const window = (new JSDOM()).window;
const { HTMLElement, customElements, document } = window;

class CustomElement extends HTMLElement {
  constructor() {
    super();

    console.log('constructed');
  }

  connectedCallback() {
    console.log('connected');
  }
}

customElements.define('custom-element', CustomElement);
document.body.appendChild(new CustomElement());
//constructed
//connected

{
  const window = (new JSDOM()).window;
  const { HTMLElement, customElements, document } = window;

  class CustomElement extends HTMLElement {
    constructor() {
      super();

      console.log('Constructed');
    }

    connectedCallback() {
      console.log('Connected');
    }
  }

  customElements.define('custom-element', CustomElement);
  document.body.appendChild(new CustomElement());
  //constructed
  //connected
}

Фактически это то, что моя тестовая система делает, но ломается. Так что это может быть что-то на моей стороне.

Редактировать:

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

  // Inside the block, second component, reuse the HTMLElement.
  const { customElements, document } = window;

Это приведет к следующему:

connected
/home/tbranyen/git/pmdartus/jsdom/lib/jsdom/living/helpers/create-element.js:643
        throw new TypeError("Illegal constructor");

Редактировать 2:

Нашел это:

  // Don't reuse the previously defined Element...
  global.HTMLElement = global.HTMLElement || jsdom.window.HTMLElement;

Заметив, что этой ветке уже 4 года, поддерживаются или планируются ли веб-компоненты?

Было бы неплохо иметь в этом веб-компоненты, но в качестве альтернативы, если кто-то хотел бы знать .... безголовый хром теперь можно использовать в узле для рендеринга/сборки файла html sting.

Заметив, что этой ветке уже 4 года, поддерживаются или планируются ли веб-компоненты?

Работа продолжается, так как спецификация реализуется по частям.

Полифил по адресу: https://github.com/WebReflection/document-register-element Работает как шарм! Моя самая искренняя благодарность автору!

Для тех, кто борется с той же проблемой, просто выполните:

npm install -D document-register-element

В вашей конфигурации jest установите установочный файл, который будет запускаться перед всеми вашими тестами:

{ "setupFilesAfterEnv": [ "./tests/setup.js" ] }

И, наконец, внутри этого файла ('tests/setup.js'):

import 'document-register-element'

После этого регистрация и создание пользовательских элементов в jsdom через document.createElement('custom-component') работает отлично! Однако фрагменты, похоже, не работают. (Кстати, я не использую Shadow Dom).

@tebanep , как вы упомянули, что polyfill не подходит для большинства работ с веб-компонентами, если он не поддерживает Shadow DOM, то на самом деле это не сравнение с тем, что он делает.

@tebanep Спасибо. Поскольку мне не нужен теневой дом, это отличная информация

Есть надежда, что это будет реализовано? Прямо сейчас мы используем jsdom-wc с множеством ошибок, но лучшего решения у нас нет. Моя надежда и молитва на эту тему.

@dknight Я знаю, что jsdom-wc - это в значительной степени хак, чтобы заставить его работать. Я опубликовал модуль со значительно лучшей совместимостью в своей личной области npm. Вы можете установить его с помощью:

npm install @tbranyen/[email protected] --save-dev

Я использую это сейчас для всех своих потребностей веб-компонента JSDOM, пока не появится стабильная версия.

@tbranyen Вы отменили публикацию своего форка? Я не могу найти его в npm.

@KingHenne dangit, похоже, он попал в наш «корпоративный» реестр. Я только что опубликовал для публики npm. Извини за это!

Не @ меня, но разве мы не должны просто протестировать код веб-интерфейса в реальном браузере, например, с помощью puppeteer. После этого проблема поддержки Shadow DOM/Custom Elements исчезнет.

Не оставляйте комментарий, если вы не хотите быть @'d @Georgegriff. Это правильная стратегия, но в других отношениях она медленная и глючная, поскольку вы эффективно выполняете IPC, да, даже с кукловодом. Когда браузер умирает, во многих случаях непонятно почему. Просто попробуйте решить проблемы с кукловодом в шутку, чтобы понять, почему это не всегда лучшая идея.

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

Полифил по адресу: https://github.com/WebReflection/document-register-element Работает как шарм! Моя самая искренняя благодарность автору!

Для тех, кто борется с той же проблемой, просто выполните:

npm install -D document-register-element

В вашей конфигурации jest установите установочный файл, который будет запускаться перед всеми вашими тестами:

{ "setupFilesAfterEnv": [ "./tests/setup.js" ] }

И, наконец, внутри этого файла ('tests/setup.js'):

import 'document-register-element'

После этого регистрация и создание пользовательских элементов в jsdom через document.createElement('custom-component') работает отлично! Однако фрагменты, похоже, не работают. (Кстати, я не использую Shadow Dom).

У меня это работает нормально, но connectedCallback никогда не вызывается, есть идеи?

@FaBeyyy то же самое для меня :(

@FaBeyyy @majo44 вы должны добавить свой компонент в документ, т.е. document.body.appendChild(...) за connectedCallback , чтобы быть уволенным. По спецификациям он вызывается, когда компонент прикреплен к Дому.

На данный момент JSDOM фактически является браузером, но не таким стабильным, как большая тройка.

На данный момент это больше похоже на большую двойку, потому что Microsoft отказывается от своей, которая была с ними столько же, сколько и Windows.

@FaBeyyy @majo44 вы должны добавить свой компонент в документ, т.е. document.body.appendChild(...) за connectedCallback , чтобы быть уволенным. По спецификациям он вызывается, когда компонент прикреплен к Дому.

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

@FaBeyyy
Так что я нашел установку, которая работает для меня. Мне пришлось добавить полифилл для MutationObserver. Я использую JSDOM для тестирования морской свинки с помощью Jest, и рабочая настройка такова:

// package.json
{  ...
  "jest": {
    "transform": {
      "^.+\\.(mjs|jsx|js)$": "babel-jest"
    },
    "setupFiles": [
      "<rootDir>/node_modules/babel-polyfill/dist/polyfill.js",
      "<rootDir>/node_modules/mutationobserver-shim/dist/mutationobserver.min.js",
      "<rootDir>/node_modules/document-register-element/build/document-register-element.node.js"
    ]
  }
... 
}
//.bablerc
{
    "presets": [
        ["@babel/preset-env", { "modules": "commonjs"}]
    ]
}

@FaBeyyy
Так что я нашел установку, которая работает для меня. Мне пришлось добавить полифилл для MutationObserver. Я использую JSDOM для тестирования морской свинки с помощью Jest, и рабочая настройка такова:

// package.json
{  ...
  "jest": {
    "transform": {
      "^.+\\.(mjs|jsx|js)$": "babel-jest"
    },
    "setupFiles": [
      "<rootDir>/node_modules/babel-polyfill/dist/polyfill.js",
      "<rootDir>/node_modules/mutationobserver-shim/dist/mutationobserver.min.js",
      "<rootDir>/node_modules/document-register-element/build/document-register-element.node.js"
    ]
  }
... 
}

//.bablerc { "presets": [ ["@babel/preset-env", { "modules": "commonjs"}] ] }

Хорошо, спасибо!

@ majo44 это не требуется для последней версии jsdom. При работе с Jest (который использует jsdom v11) вы можете просто использовать обновленную среду: https://www.npmjs.com/package/jest-environment-jsdom-fourteen .

@mgibas спасибо, с jest-environment-jsdom-fourteen это также работает нормально, и полифилл наблюдателя мутаций не требуется (но версия 0.1.0, пакет с одним коммитом :))

Есть ли разбивка API-интерфейсов веб-компонентов, которые в настоящее время поддерживаются JSDOM? Похоже, теневой DOM поддерживается, но не настраиваемые элементы (по крайней мере, в ветке/репозитории релиза)?

npm install @tbranyen/[email protected] --save-dev

@tbranyen у вас есть где-нибудь исходный код вашего форка? Интересно было бы глянуть на диф 🙂

Я использую jest-environment-jsdom-fifteen, как предложил @majo44 , и элемент babel-polyfill и document-register-element (см. ответы @mgibas ). Но я все еще получаю сообщение об ошибке, когда пытаюсь получить свой теневой дом веб-компонента для тестов.

el.shadowRoot равно нулю с:

const el;
beforeEach(async() => {
  const tag= 'my-component'
  const myEl = document.createElement(tag);
  document.body.appendChild(myEl);
  await customElements.whenDefined(tag);
  await new Promise(resolve => requestAnimationFrame(() => resolve()));
  el = document.querySelector(tag);
}

it(() => {
  const fooContent = el.shadowRoot.querySelectorAll('slot[name=foo] > *');
})

Любая идея обходного пути? К вашему сведению, он уже был протестирован с Karma, Mocha, Chai & Jasmine.

Ничего особенного в моем компоненте:

customElements.define(
  'my-component',
  class extends HTMLElement {
    constructor() {
      super();

      const shadowRoot = this.attachShadow({ mode: 'open' });
      ...
  }
})

Редактировать

Я провел некоторую отладку с помощью jsdom 15.1.1, чтобы лучше понять мою проблему.
И все же я не понимаю, почему здесь ноль...

Итак, Element.shadowRoot реализован начиная с 88e72ef.
https://github.com/jsdom/jsdom/blob/1951a19d8d40bc196cfda62a8dafa76ddda6a0d2/lib/jsdom/living/nodes/Element-impl.js#L388 -L415

После document.createElement этот ._shadowDom в порядке на https://github.com/jsdom/jsdom/blob/15.1.1/lib/jsdom/living/nodes/Element-impl.js#L403. И каждый элемент в теневом доме создается (конструктор элементов вызывается с правильными значениями).

Но когда я вызываю el.shadowDom сразу после document.body.appendChild(el) (https://github.com/jsdom/jsdom/blob/15.1.1/lib/jsdom/living/nodes/Element-impl .js#L408), this. _shadowRoot равно null!

То же самое после

 await customElements.whenDefined(tag);
 await new Promise(resolve => requestAnimationFrame(() => resolve()));

Или даже если я использую следующее вместо document.

document.body.innerHTML = `
  <my-component id="fixture"></my-component>
`:

Для воспроизведения см.:
https://github.com/noelmace/devcards/tree/jest

@nminhnguyen Я думаю, вы можете найти исходный код форка, сделанного @tbranyan , здесь https://github.com/tbranyen/jsdom/tree/initial-custom-elements-impl

Я пытаюсь протестировать веб-компоненты, созданные с помощью lit-html и lit-element, и я заметил эту разницу при создании элементов.

const myElem = new MyElem();

document.body.appendChild(myElem);
await myElem.updateComplete;

console.log(myElem.shadowRoot) // exists and has the rendered markup

и когда я использую document.createElement

const myElem = document.createElement('my-elem');

document.body.appendChild(myElem);
await myElem.updateComplete;

console.log(myElem.shadowRoot) // null

Для настройки jest я использую только один полифилл: setupFiles: ['document-register-element']

Кажется, что метод рендеринга в myElem никогда не вызывается. Продолжая отладку, я обнаружил, что метод initialize , находящийся в элементе lit, никогда не вызывается.
Итак, второй пример будет работать, если я это сделаю

const myElem = document.createElement('my-elem');
myElem.initialize();

document.body.appendChild(myElem);
await myElem.updateComplete;

console.log(myElem.shadowRoot) //  exists and has the rendered markup

Я создал альтернативный DOM, поддерживающий веб-компоненты. Сначала я пытался сделать PR, но то, как работает JSDOM, мешало мне решать там свои задачи. Вы можете использовать его или посмотреть код.

ДОМ:
https://www.npmjs.com/package/счастливый дом

Шуточная среда:
https://www.npmjs.com/package/jest-environment-happy-dom

Выглядит потрясающе. Спасибо.

В понедельник, 7 октября 2019 г., 1:08 [email protected] написал:

Я создал альтернативный DOM, поддерживающий веб-компоненты. я первый
пытался сделать PR, но то, как работает JSDOM, затруднило мне решение моего
потребности там. Вы можете использовать его и/или посмотреть код.

ДОМ:
https://www.npmjs.com/package/счастливый дом

Шуточная среда:
https://www.npmjs.com/package/jest-environment-happy-dom


Вы получаете это, потому что подписаны на эту тему.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/jsdom/jsdom/issues/1030?email_source=notifications&email_token=ACQ5ZD5QUEITPND4SXWOHW3QNILSRA5CNFSM4A4G5SF2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAOO5ZA5876,63issuecomment-
или заглушить тему
https://github.com/notifications/unsubscribe-auth/ACQ5ZDYU465DXI4KHBQH4KTQNILSRANCNFSM4A4G5SFQ
.

@козерог86
Ваша работа делает мою тестовую среду простой, спасибо!
https://github.com/EasyWebApp/WebCell/tree/v2/MobX

@козерог86
Ваша работа делает мою тестовую среду простой, спасибо!
https://github.com/EasyWebApp/WebCell/tree/v2/MobX

Спасибо @TechQuery!

Выглядит потрясающе. Спасибо.

Пн, 7 октября 2019 г., 1:08 capricorn86 @ . * > писал(а): Я создал альтернативный DOM, поддерживающий веб-компоненты. Сначала я пытался сделать PR, но то, как работает JSDOM, мешало мне решать там свои задачи. Вы можете использовать его и/или посмотреть код. DOM: https://www.npmjs.com/package/happy-dom Среда Jest: https://www.npmjs.com/package/jest-environment-happy-dom — вы получаете это, потому что подписаны на это нить. Ответить на это сообщение непосредственно, просматривать его на GitHub <# 1030? Email_source = уведомления и email_token = ACQ5ZD5QUEITPND4SXWOHW3QNILSRA5CNFSM4A4G5SF2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAOO5ZA # issuecomment-538767076>, или приглушить нить https://github.com/notifications/unsubscribe-auth/ACQ5ZDYU465DXI4KHBQH4KTQNILSRANCNFSM4A4G5SFQ .

Спасибо @mots!

Есть ли разбивка API-интерфейсов веб-компонентов, которые в настоящее время поддерживаются JSDOM? Похоже, теневой DOM поддерживается, но не настраиваемые элементы (по крайней мере, в ветке/репозитории релиза)?

Меня бы это тоже заинтересовало :)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги