return function()
	local createElement = require(script.Parent.Parent.createElement)
	local createReconciler = require(script.Parent.Parent.createReconciler)
	local createSpy = require(script.Parent.Parent.createSpy)
	local NoopRenderer = require(script.Parent.Parent.NoopRenderer)
	local GlobalConfig = require(script.Parent.Parent.GlobalConfig)

	local Component = require(script.Parent.Parent.Component)

	local noopReconciler = createReconciler(NoopRenderer)

	it("should be invoked when mounted", function()
		local config = {
			propValidation = true,
		}

		GlobalConfig.scoped(config, function()
			local MyComponent = Component:extend("MyComponent")

			local validatePropsSpy = createSpy(function()
				return true
			end)

			MyComponent.validateProps = validatePropsSpy.value

			function MyComponent:render()
				return nil
			end

			local element = createElement(MyComponent)
			local hostParent = nil
			local key = "Test"

			noopReconciler.mountVirtualNode(element, hostParent, key)
			expect(validatePropsSpy.callCount).to.equal(1)
		end)
	end)

	it("should be invoked when props change", function()
		local config = {
			propValidation = true,
		}

		GlobalConfig.scoped(config, function()
			local MyComponent = Component:extend("MyComponent")

			local validatePropsSpy = createSpy(function()
				return true
			end)

			MyComponent.validateProps = validatePropsSpy.value

			function MyComponent:render()
				return nil
			end

			local element = createElement(MyComponent, { a = 1 })
			local hostParent = nil
			local key = "Test"

			local node = noopReconciler.mountVirtualNode(element, hostParent, key)
			expect(validatePropsSpy.callCount).to.equal(1)
			validatePropsSpy:assertCalledWithDeepEqual({
				a = 1,
			})

			local newElement = createElement(MyComponent, { a = 2 })
			noopReconciler.updateVirtualNode(node, newElement)
			expect(validatePropsSpy.callCount).to.equal(2)
			validatePropsSpy:assertCalledWithDeepEqual({
				a = 2,
			})
		end)
	end)

	it("should not be invoked when state changes", function()
		local config = {
			propValidation = true,
		}

		GlobalConfig.scoped(config, function()
			local MyComponent = Component:extend("MyComponent")

			local setStateCallback = nil
			local validatePropsSpy = createSpy(function()
				return true
			end)

			MyComponent.validateProps = validatePropsSpy.value

			function MyComponent:init()
				setStateCallback = function(newState)
					self:setState(newState)
				end
			end

			function MyComponent:render()
				return nil
			end

			local element = createElement(MyComponent, { a = 1 })
			local hostParent = nil
			local key = "Test"

			noopReconciler.mountVirtualNode(element, hostParent, key)
			expect(validatePropsSpy.callCount).to.equal(1)
			validatePropsSpy:assertCalledWithDeepEqual({
				a = 1
			})

			setStateCallback({
				b = 1
			})

			expect(validatePropsSpy.callCount).to.equal(1)
		end)
	end)

	it("should throw if validateProps is not a function", function()
		local config = {
			propValidation = true,
		}

		GlobalConfig.scoped(config, function()
			local MyComponent = Component:extend("MyComponent")
			MyComponent.validateProps = 1

			function MyComponent:render()
				return nil
			end

			local element = createElement(MyComponent)
			local hostParent = nil
			local key = "Test"

			expect(function()
				noopReconciler.mountVirtualNode(element, hostParent, key)
			end).to.throw()
		end)
	end)

	it("should throw if validateProps returns false", function()
		local config = {
			propValidation = true,
		}

		GlobalConfig.scoped(config, function()
			local MyComponent = Component:extend("MyComponent")
			MyComponent.validateProps = function()
				return false
			end

			function MyComponent:render()
				return nil
			end

			local element = createElement(MyComponent)
			local hostParent = nil
			local key = "Test"

			expect(function()
				noopReconciler.mountVirtualNode(element, hostParent, key)
			end).to.throw()
		end)
	end)

	it("should include the component name in the error message", function()
		local config = {
			propValidation = true,
		}

		GlobalConfig.scoped(config, function()
			local MyComponent = Component:extend("MyComponent")
			MyComponent.validateProps = function()
				return false
			end

			function MyComponent:render()
				return nil
			end

			local element = createElement(MyComponent)
			local hostParent = nil
			local key = "Test"

			local success, error = pcall(function()
				noopReconciler.mountVirtualNode(element, hostParent, key)
			end)

			expect(success).to.equal(false)
			local startIndex = error:find("MyComponent")
			expect(startIndex).to.be.ok()
		end)
	end)

	it("should be invoked after defaultProps are applied", function()
		local config = {
			propValidation = true,
		}

		GlobalConfig.scoped(config, function()
			local MyComponent = Component:extend("MyComponent")

			local validatePropsSpy = createSpy(function()
				return true
			end)

			MyComponent.validateProps = validatePropsSpy.value

			function MyComponent:render()
				return nil
			end

			MyComponent.defaultProps = {
				b = 2,
			}

			local element = createElement(MyComponent, { a = 1 })
			local hostParent = nil
			local key = "Test"

			local node = noopReconciler.mountVirtualNode(element, hostParent, key)
			expect(validatePropsSpy.callCount).to.equal(1)
			validatePropsSpy:assertCalledWithDeepEqual({
				a = 1,
				b = 2,
			})

			local newElement = createElement(MyComponent, { a = 2 })
			noopReconciler.updateVirtualNode(node, newElement)
			expect(validatePropsSpy.callCount).to.equal(2)
			validatePropsSpy:assertCalledWithDeepEqual({
				a = 2,
				b = 2,
			})
		end)
	end)

	it("should not be invoked if the flag is off", function()
		local config = {
			propValidation = false,
		}

		GlobalConfig.scoped(config, function()
			local MyComponent = Component:extend("MyComponent")

			local validatePropsSpy = createSpy(function()
				return true
			end)

			MyComponent.validateProps = validatePropsSpy.value

			function MyComponent:render()
				return nil
			end

			local element = createElement(MyComponent, { a = 1 })
			local hostParent = nil
			local key = "Test"

			local node = noopReconciler.mountVirtualNode(element, hostParent, key)
			expect(validatePropsSpy.callCount).to.equal(0)

			local newElement = createElement(MyComponent, { a = 2 })
			noopReconciler.updateVirtualNode(node, newElement)
			expect(validatePropsSpy.callCount).to.equal(0)
		end)
	end)
end