Chromium Code Reviews| Index: third_party/WebKit/Source/core/dom/custom/CustomElementsRegistryTest.cpp |
| diff --git a/third_party/WebKit/Source/core/dom/custom/CustomElementsRegistryTest.cpp b/third_party/WebKit/Source/core/dom/custom/CustomElementsRegistryTest.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6c8b50b0d16cc13e4dc0e5325da54b99c40ed643 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/core/dom/custom/CustomElementsRegistryTest.cpp |
| @@ -0,0 +1,310 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "core/dom/custom/CustomElementsRegistry.h" |
| + |
| +#include "bindings/core/v8/ExceptionState.h" |
| +#include "core/dom/Document.h" |
| +#include "core/dom/Element.h" |
| +#include "core/dom/ElementRegistrationOptions.h" |
| +#include "core/dom/custom/CustomElementDefinition.h" |
| +#include "core/dom/custom/CustomElementDefinitionBuilder.h" |
| +#include "core/dom/custom/CustomElementDescriptor.h" |
| +#include "core/dom/custom/CustomElementTestHelpers.h" |
| +#include "core/dom/shadow/ShadowRoot.h" |
| +#include "core/dom/shadow/ShadowRootInit.h" |
| +#include "core/html/HTMLDocument.h" |
| +#include "core/testing/DummyPageHolder.h" |
| +#include "platform/ScriptForbiddenScope.h" |
| +#include "platform/heap/Handle.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "wtf/OwnPtr.h" |
| +#include "wtf/text/AtomicString.h" |
| + |
| +namespace blink { |
| + |
| +class CustomElementsRegistryTestBase : public ::testing::Test { |
| +public: |
|
yosin_UTC9
2016/06/01 06:15:45
No need to have public accessibility for Test clas
dominicc (has gone to gerrit)
2016/06/01 23:30:57
I suspect you're wrong about this; fixtures using
yosin_UTC9
2016/06/02 02:02:18
Sorry, my explanation is bad. I mean all members o
|
| + virtual Document& document() = 0; |
| + virtual CustomElementsRegistry& registry() = 0; |
| + |
| + void collectCandidates( |
| + const CustomElementDescriptor& desc, |
| + HeapVector<Member<Element>>* elements) |
| + { |
| + registry().collectCandidates(desc, elements); |
| + } |
| +}; |
| + |
| +class CustomElementsRegistryTest : public CustomElementsRegistryTestBase { |
| +public: |
|
yosin_UTC9
2016/06/01 06:15:44
No need to have public accessibility for Test clas
|
| + Document& document() override { return *m_document; } |
| + CustomElementsRegistry& registry() override { return *m_registry; } |
| + |
| +protected: |
| + void SetUp() override |
| + { |
| + CustomElementsRegistryTestBase::SetUp(); |
| + |
| + m_document = HTMLDocument::create(); |
| + m_document->appendChild(CreateElement("html").inDocument(m_document)); |
| + |
| + m_registry = CustomElementsRegistry::create(m_document); |
| + } |
| + |
| + void TearDown() override |
| + { |
| + m_document = nullptr; |
| + m_registry = nullptr; |
| + CustomElementsRegistryTestBase::TearDown(); |
| + } |
| + |
| +private: |
| + Persistent<Document> m_document; |
| + Persistent<CustomElementsRegistry> m_registry; |
| +}; |
| + |
| +class CustomElementsRegistryFrameTest : public CustomElementsRegistryTestBase { |
| +public: |
|
yosin_UTC9
2016/06/01 06:15:45
No need to have public accessibility for Test clas
|
| + Document& document() override { return m_page->document(); } |
| + CustomElementsRegistry& registry() override |
| + { |
| + return *m_page->frame().localDOMWindow()->customElements(); |
| + } |
| + ScriptState* scriptState() |
| + { |
| + return ScriptState::forMainWorld(&m_page->frame()); |
| + } |
| + |
| + ShadowRoot* attachShadowTo(Element* element) |
| + { |
| + NonThrowableExceptionState noExceptions; |
| + ShadowRootInit shadowRootInit; |
| + return |
| + element->attachShadow(scriptState(), shadowRootInit, noExceptions); |
| + } |
| + |
| +protected: |
| + void SetUp() override |
| + { |
| + CustomElementsRegistryTestBase::SetUp(); |
| + m_page = DummyPageHolder::create(IntSize(1, 1)); |
| + } |
| + |
| + void TearDown() override |
| + { |
| + m_page = nullptr; |
| + CustomElementsRegistryTestBase::TearDown(); |
| + } |
| + |
| +private: |
| + OwnPtr<DummyPageHolder> m_page; |
|
yosin_UTC9
2016/06/01 06:15:45
nit: Let's use std::unique_ptr<DummyPageHolder>
|
| +}; |
| + |
| +TEST_F( |
| + CustomElementsRegistryTest, |
| + collectCandidates_shouldNotIncludeElementsRemovedFromDocument) |
| +{ |
| + Element* element = CreateElement("a-a").inDocument(&document()); |
| + registry().addCandidate(element); |
| + |
| + HeapVector<Member<Element>> elements; |
| + collectCandidates( |
| + CustomElementDescriptor("a-a", "a-a"), |
| + &elements); |
| + |
| + EXPECT_EQ(0u, elements.size()) |
|
yosin_UTC9
2016/06/01 06:15:45
nit: isEmpty() is more readable.
dominicc (has gone to gerrit)
2016/06/01 23:30:57
True; this produces a more useful failure. With is
yosin_UTC9
2016/06/02 02:02:18
How about:
EXPECT_TRUE(elements.isEmpty()) << "no
|
| + << "no candidates should have been found"; |
| + EXPECT_FALSE(elements.contains(element)) |
| + << "the out-of-document candidate should not have been found"; |
| +} |
| + |
| +TEST_F( |
| + CustomElementsRegistryTest, |
| + collectCandidates_shouldNotIncludeElementsInDifferentDocument) |
| +{ |
| + Element* element = CreateElement("a-a").inDocument(&document()); |
| + registry().addCandidate(element); |
| + |
| + Document* otherDocument = HTMLDocument::create(); |
| + otherDocument->appendChild(element); |
| + EXPECT_EQ(otherDocument, element->ownerDocument()) |
| + << "sanity: another document should have adopted an element on append"; |
| + |
| + HeapVector<Member<Element>> elements; |
| + collectCandidates( |
| + CustomElementDescriptor("a-a", "a-a"), |
| + &elements); |
| + |
| + EXPECT_EQ(0u, elements.size()) |
| + << "no candidates should have been found"; |
| + EXPECT_FALSE(elements.contains(element)) |
| + << "the adopted-away candidate should not have been found"; |
| +} |
| + |
| +TEST_F( |
| + CustomElementsRegistryTest, |
| + collectCandidates_shouldOnlyIncludeCandidatesMatchingDescriptor) |
| +{ |
| + CustomElementDescriptor descriptor("hello-world", "hello-world"); |
| + |
| + // Does not match: namespace is not HTML |
| + Element* a = CreateElement("hello-world") |
|
yosin_UTC9
2016/06/01 06:15:45
Please avoid to use one letter variable name.
|
| + .inDocument(&document()) |
| + .inNamespace("data:text/date,1981-03-10"); |
| + // Matches |
| + Element* b = CreateElement("hello-world").inDocument(&document()); |
| + // Does not match: local name is not hello-world |
| + Element* c = CreateElement("button") |
| + .inDocument(&document()) |
| + .withIsAttribute("hello-world"); |
| + document().documentElement()->appendChild(a); |
| + a->appendChild(b); |
| + a->appendChild(c); |
| + |
| + registry().addCandidate(a); |
| + registry().addCandidate(b); |
| + registry().addCandidate(c); |
| + |
| + HeapVector<Member<Element>> elements; |
| + collectCandidates(descriptor, &elements); |
| + |
| + EXPECT_EQ(1u, elements.size()) |
| + << "only one candidates should have been found"; |
| + EXPECT_EQ(b, elements[0]) |
| + << "the matching element should have been found"; |
| +} |
| + |
| +TEST_F(CustomElementsRegistryTest, collectCandidates_oneCandidate) |
| +{ |
| + Element* element = CreateElement("a-a").inDocument(&document()); |
| + registry().addCandidate(element); |
| + document().documentElement()->appendChild(element); |
| + |
| + HeapVector<Member<Element>> elements; |
| + collectCandidates( |
| + CustomElementDescriptor("a-a", "a-a"), |
| + &elements); |
| + |
| + EXPECT_EQ(1u, elements.size()) |
| + << "exactly one candidate should have been found"; |
| + EXPECT_TRUE(elements.contains(element)) |
| + << "the candidate should be the element that was added"; |
| +} |
| + |
| +TEST_F(CustomElementsRegistryTest, collectCandidates_shouldBeInDocumentOrder) |
| +{ |
| + CreateElement& factory = CreateElement("a-a").inDocument(&document()); |
| + Element* a = factory.withId("a"); |
| + Element* b = factory.withId("b"); |
| + Element* c = factory.withId("c"); |
| + |
| + registry().addCandidate(b); |
| + registry().addCandidate(a); |
| + registry().addCandidate(c); |
| + |
| + document().documentElement()->appendChild(a); |
| + a->appendChild(b); |
| + document().documentElement()->appendChild(c); |
| + |
| + HeapVector<Member<Element>> elements; |
| + collectCandidates( |
| + CustomElementDescriptor("a-a", "a-a"), |
| + &elements); |
| + |
| + EXPECT_EQ(a, elements[0].get()); |
| + EXPECT_EQ(b, elements[1].get()); |
| + EXPECT_EQ(c, elements[2].get()); |
| +} |
| + |
| +class TestCustomElementDefinition : public CustomElementDefinition { |
| + WTF_MAKE_NONCOPYABLE(TestCustomElementDefinition); |
| +public: |
| + TestCustomElementDefinition(const CustomElementDescriptor& descriptor) |
| + : CustomElementDefinition(descriptor) |
| + { |
| + } |
| + |
| + virtual ~TestCustomElementDefinition() = default; |
| + |
| + bool runConstructor(Element* element) override |
| + { |
| + if (constructionStack().isEmpty() |
| + || constructionStack().last() != element) |
| + return false; |
| + constructionStack().last().clear(); |
| + return true; |
| + } |
| +}; |
| + |
| +// Classes which use trace macros cannot be local because of the |
| +// traceImpl template. |
| +class LogUpgradeDefinition : public TestCustomElementDefinition { |
| + WTF_MAKE_NONCOPYABLE(LogUpgradeDefinition); |
| +public: |
| + LogUpgradeDefinition(const CustomElementDescriptor& descriptor) |
| + : TestCustomElementDefinition(descriptor) |
| + { |
| + } |
| + |
| + DEFINE_INLINE_VIRTUAL_TRACE() |
| + { |
| + TestCustomElementDefinition::trace(visitor); |
| + visitor->trace(m_element); |
| + } |
| + |
| + // TODO(dominicc): Make this class collect a vector of what's |
| + // upgraded; it will be useful in more tests. |
| + Member<Element> m_element; |
| + uint32_t m_invocationCount; |
| + |
| + bool runConstructor(Element* element) override |
| + { |
| + m_invocationCount++; |
| + m_element = element; |
| + return TestCustomElementDefinition::runConstructor(element); |
| + } |
| +}; |
| + |
| +class LogUpgradeBuilder final : public CustomElementDefinitionBuilder { |
| + STACK_ALLOCATED(); |
| + WTF_MAKE_NONCOPYABLE(LogUpgradeBuilder); |
| +public: |
| + LogUpgradeBuilder() { } |
| + |
| + bool checkConstructorIntrinsics() override { return true; } |
| + bool checkConstructorNotRegistered() override { return true; } |
| + bool checkPrototype() override { return true; } |
| + CustomElementDefinition* build( |
| + const CustomElementDescriptor& descriptor) { |
| + return new LogUpgradeDefinition(descriptor); |
| + } |
| +}; |
| + |
| +TEST_F(CustomElementsRegistryFrameTest, define_upgradesInDocumentElements) |
| +{ |
| + ScriptForbiddenScope doNotRelyOnScript; |
| + |
| + Element* element = CreateElement("a-a").inDocument(&document()); |
| + document().documentElement()->appendChild(element); |
| + |
| + LogUpgradeBuilder builder; |
| + NonThrowableExceptionState shouldNotThrow; |
| + registry().define( |
| + "a-a", |
| + builder, |
| + ElementRegistrationOptions(), |
| + shouldNotThrow); |
| + LogUpgradeDefinition* definition = |
| + static_cast<LogUpgradeDefinition*>(registry().definitionForName("a-a")); |
| + EXPECT_EQ(1u, definition->m_invocationCount) |
| + << "defining the element should have 'upgraded' the existing element"; |
| + EXPECT_EQ(element, definition->m_element) |
| + << "the existing a-a element should have been upgraded"; |
| +} |
| + |
| +// TODO(dominicc): Add tests which adjust the "is" attribute when type |
| +// extensions are implemented. |
| + |
| +} // namespace blink |