-- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/jest-react/src/JestReact.js
--[[*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 ]]
local Packages = script.Parent.Parent
local ReactGlobals = require(Packages.ReactGlobals)
local LuauPolyfill = require(Packages.LuauPolyfill)
local Array = LuauPolyfill.Array
-- ROBLOX deviation START: not used
-- local Boolean = LuauPolyfill.Boolean
-- ROBLOX deviation END
local Object = LuauPolyfill.Object
local exports = {}
-- ROBLOX deviation START: fix import
-- local JestGlobals = require(Packages.Dev.JestGlobals)
local JestGlobals = require(Packages.JestGlobals)
local expect = JestGlobals.expect
-- ROBLOX deviation END
-- ROBLOX deviation START: fix import
-- local sharedReactSymbolsModule = require(Packages.shared.ReactSymbols)
local sharedReactSymbolsModule = require(Packages.Shared).ReactSymbols
-- ROBLOX deviation END
local REACT_ELEMENT_TYPE = sharedReactSymbolsModule.REACT_ELEMENT_TYPE
local REACT_FRAGMENT_TYPE = sharedReactSymbolsModule.REACT_FRAGMENT_TYPE
-- ROBLOX deviation START: fix import
-- local invariant = require(Packages.shared.invariant).default
local invariant = require(Packages.Shared).invariant
-- ROBLOX deviation END
-- ROBLOX deviation START: predeclare variables
local jsonChildToJSXChild, jsonChildrenToJSXChildren
-- ROBLOX deviation END
local function captureAssertion(fn)
	-- Trick to use a Jest matcher inside another Jest matcher. `fn` contains an
	-- assertion; if it throws, we capture the error and return it, so the stack
	-- trace presented to the user points to the original assertion in the
	-- test file.
	do --[[ ROBLOX COMMENT: try-catch block conversion ]]
		-- ROBLOX deviation START: use pcall, format message
		-- local ok, result, hasReturned = xpcall(function()
		-- 	fn()
		-- end, function(error_)
		-- 	return {
		-- 		pass = false,
		-- 		message = function()
		-- 			return error_.message
		-- 		end,
		-- 	},
		-- 		true
		-- end)
		-- if hasReturned then
		-- 	return result
		-- end
		local ok, result = pcall(fn)

		if not ok then
			local stringResult = tostring(result)
			local subMessageIndex = string.find(stringResult, " ")
			local message = string.sub(stringResult, subMessageIndex + 1)

			return {
				pass = false,
				message = function()
					return message
				end,
			}
		end
		-- ROBLOX deviation END
	end
	return { pass = true }
end
local function assertYieldsWereCleared(root)
	local Scheduler = root._Scheduler
	-- ROBLOX deviation START: use dot notation
	-- local actualYields = Scheduler:unstable_clearYields()
	local actualYields = Scheduler.unstable_clearYields()
	-- ROBLOX deviation END
	invariant(
			-- ROBLOX deviation START: fix .length
			-- actualYields.length == 0,
		#actualYields == 0,
		-- ROBLOX deviation END
		"Log of yielded values is not empty. "
			.. "Call expect(ReactTestRenderer).unstable_toHaveYielded(...) first."
	)
end
local function unstable_toMatchRenderedOutput(root, expectedJSX)
	assertYieldsWereCleared(root)
	-- ROBLOX deviation START: use dot notation
	-- local actualJSON = root:toJSON()
	local actualJSON = root.toJSON()
	-- ROBLOX deviation END
	local actualJSX
	if actualJSON == nil or typeof(actualJSON) == "string" then
		actualJSX = actualJSON
		-- ROBLOX deviation START: remove toJSBoolean
		-- elseif Boolean.toJSBoolean(Array.isArray(actualJSON)) then
	elseif Array.isArray(actualJSON) then
		-- ROBLOX deviation END
		-- ROBLOX deviation START: fix .length
		-- if actualJSON.length == 0 then
		if #actualJSON == 0 then
			-- ROBLOX deviation END
			actualJSX = nil
		-- ROBLOX deviation START: fix .length
		-- elseif actualJSON.length == 1 then
		elseif #actualJSON == 1 then
			-- ROBLOX deviation END
			actualJSX = jsonChildToJSXChild(actualJSON[
				1 --[[ ROBLOX adaptation: added 1 to array index ]]
			])
		else
			local actualJSXChildren = jsonChildrenToJSXChildren(actualJSON)
			if actualJSXChildren == nil or typeof(actualJSXChildren) == "string" then
				actualJSX = actualJSXChildren
			else
				actualJSX = {
					["$$typeof"] = REACT_ELEMENT_TYPE,
					type = REACT_FRAGMENT_TYPE,
					key = nil,
					ref = nil,
					props = { children = actualJSXChildren },
					_owner = nil,
					-- ROBLOX deviation START: remove toJSBoolean, use ReactGlobals.__DEV__
					-- _store = if Boolean.toJSBoolean(__DEV__) then {} else nil,
					_store = if ReactGlobals.__DEV__ then {} else nil,
					-- ROBLOX deviation END
				}
			end
		end
	else
		actualJSX = jsonChildToJSXChild(actualJSON)
	end
	return captureAssertion(function()
		expect(actualJSX).toEqual(expectedJSX)
	end)
end
exports.unstable_toMatchRenderedOutput = unstable_toMatchRenderedOutput
-- ROBLOX deviation START: predeclared function
-- local function jsonChildToJSXChild(jsonChild)
function jsonChildToJSXChild(jsonChild)
	-- ROBLOX deviation END
	if jsonChild == nil or typeof(jsonChild) == "string" then
		return jsonChild
	else
		local jsxChildren = jsonChildrenToJSXChildren(jsonChild.children)
		return {
			["$$typeof"] = REACT_ELEMENT_TYPE,
			type = jsonChild.type,
			key = nil,
			ref = nil,
			props = if jsxChildren == nil
				then jsonChild.props
				else Object.assign({}, jsonChild.props, { children = jsxChildren }),
			_owner = nil,
			-- ROBLOX deviation START: remove toJSBoolean, use ReactGlobals.__DEV__
			-- _store = if Boolean.toJSBoolean(__DEV__) then {} else nil,
			_store = if ReactGlobals.__DEV__ then {} else nil,
			-- ROBLOX deviation END
		}
	end
end
-- ROBLOX deviation START: predeclared function
-- local function jsonChildrenToJSXChildren(jsonChildren)
function jsonChildrenToJSXChildren(jsonChildren)
	-- ROBLOX deviation END
	if jsonChildren ~= nil then
		-- ROBLOX deviation START: fix .length
		-- if jsonChildren.length == 1 then
		if #jsonChildren == 1 then
			-- ROBLOX deviation END
			return jsonChildToJSXChild(jsonChildren[
				1 --[[ ROBLOX adaptation: added 1 to array index ]]
			])
		elseif
			-- ROBLOX deviation START: fix .length
			-- jsonChildren.length
			#jsonChildren
			-- ROBLOX deviation END
			> 1 --[[ ROBLOX CHECK: operator '>' works only if either both arguments are strings or both are a number ]]
		then
			local jsxChildren = {}
			local allJSXChildrenAreStrings = true
			local jsxChildrenString = ""
			-- ROBLOX deviation START: use in loop instead of while loop
			-- do
			-- 	local i = 0
			-- 	while
			-- 		i
			-- 		< jsonChildren.length --[[ ROBLOX CHECK: operator '<' works only if either both arguments are strings or both are a number ]]
			-- 	do
			-- 		local jsxChild = jsonChildToJSXChild(jsonChildren[tostring(i)])
			-- 		table.insert(jsxChildren, jsxChild) --[[ ROBLOX CHECK: check if 'jsxChildren' is an Array ]]
			-- 		if Boolean.toJSBoolean(allJSXChildrenAreStrings) then
			-- 			if typeof(jsxChild) == "string" then
			-- 				jsxChildrenString += jsxChild
			-- 			elseif jsxChild ~= nil then
			-- 				allJSXChildrenAreStrings = false
			-- 			end
			-- 		end
			-- 		i += 1
			-- 	end
			-- end
			for _, jsonChild in jsonChildren do
				local jsxChild = jsonChildToJSXChild(jsonChild)

				table.insert(jsxChildren, jsxChild)

				if allJSXChildrenAreStrings then
					if typeof(jsxChild) == "string" then
						jsxChildrenString = jsxChildrenString .. jsxChild
					elseif jsxChild ~= nil then
						allJSXChildrenAreStrings = false
					end
				end
			end
			-- ROBLOX deviation END
			-- ROBLOX deviation START: remove toJSBoolean
			-- return if Boolean.toJSBoolean(allJSXChildrenAreStrings)
			return if allJSXChildrenAreStrings
				-- ROBLOX deviation END
				then jsxChildrenString
				else jsxChildren
		end
	end
	return nil
end
return exports
