Skip to content
This repository has been archived by the owner on Jan 23, 2024. It is now read-only.

Add tests to hooks #30

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "vendor/roact"]
path = vendor/roact
url = git://github.com/Roblox/roact.git
[submodule "vendor/testez"]
path = vendor/testez
url = https://github.com/Roblox/testez.git
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- Provide Roact instance in hooks argument (#28)
- Added testing (#30)

### Fixed
- Fix useContext for uninitialized and/or changing contexts (#26)
Expand Down
2 changes: 1 addition & 1 deletion selene.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
std = "roblox"
std = "roblox+testez"
59 changes: 59 additions & 0 deletions src/createUseBinding.spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Roact = require(ReplicatedStorage.Roact)
local Hooks = require(ReplicatedStorage.Hooks)

local e = Roact.createElement

local function createTest(initialState, initialValue)
local test = {
renders = 0,
}

local function Test(_, hooks)
test.renders += 1
test.state, test.setState = hooks.useState(initialState)
test.binding, test.setBinding = hooks.useBinding(initialValue)
return nil
end

test.handle = Roact.mount(e(Hooks.new(Roact)(Test)))
return test
end

return function()
describe("useBinding", function()
it("should set the initial binding", function()
local test = createTest(1, 2)
expect(test.binding:getValue()).to.equal(2)
expect(test.renders).to.equal(1)
end)

it("should set to nil when passed nil", function()
local test = createTest(1)

expect(test.binding:getValue()).to.never.be.ok()

test.setBinding(2)
expect(test.binding:getValue()).to.equal(2)

test.setBinding(nil)
expect(test.binding:getValue()).to.never.be.ok()
end)

it("should not rerender when binding changes", function()
local test = createTest(1, 2)

test.setBinding(3)
expect(test.binding:getValue()).to.equal(3)
expect(test.renders).to.equal(1)
end)

it("should maintain its value after rerenders", function()
local test = createTest(1, 2)

test.setState(3)
expect(test.binding:getValue()).to.equal(2)
end)
end)
end
115 changes: 115 additions & 0 deletions src/createUseEffect.spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Roact = require(ReplicatedStorage.Roact)
local Hooks = require(ReplicatedStorage.Hooks)

local e = Roact.createElement

local function createTest(initialState, initialDependencies)
local test = {
useEffectRuns = 0,
}

local function Test(_, hooks)
test.state, test.setState = hooks.useState(initialState)
test.dependencies, test.setDependencies = hooks.useState(initialDependencies)

hooks.useEffect(function()
test.useEffectRuns += 1
end, test.dependencies)

return nil
end

test.handle = Roact.mount(e(Hooks.new(Roact)(Test)))
return test
end

return function()
describe("useEffect", function()
describe("when dependencies are nil", function()
it("should run the effect after each rerender", function()
local test = createTest(1)
expect(test.useEffectRuns).to.equal(1)

test.setState(2)
task.wait(0.1)
expect(test.useEffectRuns).to.equal(2)

test.setState(3)
task.wait(0.1)
expect(test.useEffectRuns).to.equal(3)
end)
end)

describe("when dependencies are empty", function()
it("should not run the effect after each rerender", function()
local test = createTest(1, {})
expect(test.useEffectRuns).to.equal(1)

test.setState(2)
task.wait(0.1)
expect(test.useEffectRuns).to.equal(1)
end)
end)

describe("when dependencies are constant", function()
it("should not run the effect after each rerender", function()
local test = createTest(1, { 1 })
expect(test.useEffectRuns).to.equal(1)

test.setState(2)
task.wait(0.1)
expect(test.useEffectRuns).to.equal(1)
end)
end)

describe("when dependencies are changing", function()
it("should run the effect when dependencies change", function()
local test = createTest(1, { 1 })
expect(test.useEffectRuns).to.equal(1)

test.setDependencies({ 2 })
task.wait(0.1)
expect(test.useEffectRuns).to.equal(2)

test.setState(2)
task.wait(0.1)
expect(test.useEffectRuns).to.equal(2)

test.setDependencies({ 3 })
task.wait(0.1)
expect(test.useEffectRuns).to.equal(3)
end)

it("should run the effect when dependencies change to/from nil", function()
local test = createTest(1, { 1, 2 })
expect(test.useEffectRuns).to.equal(1)

test.setDependencies({ 1, nil })
task.wait(0.1)
expect(test.useEffectRuns).to.equal(2)

test.setState(2)
task.wait(0.1)
expect(test.useEffectRuns).to.equal(2)

test.setDependencies({ 1, 2 })
task.wait(0.1)
expect(test.useEffectRuns).to.equal(3)

test.setDependencies({ nil, 2 })
task.wait(0.1)
expect(test.useEffectRuns).to.equal(4)

test.setDependencies({ 1, 2 })
task.wait(0.1)
expect(test.useEffectRuns).to.equal(5)

test.setDependencies({ nil, nil })
task.wait(0.1)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need wait? CI shouldn't need yields.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should instead just call Roact.update directly

expect(test.useEffectRuns).to.equal(6)
end)
end)
end)
end
85 changes: 85 additions & 0 deletions src/createUseMemo.spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Roact = require(ReplicatedStorage.Roact)
local Hooks = require(ReplicatedStorage.Hooks)

local e = Roact.createElement

local function createTest(initialState, initialDependencies)
local test = {}

local function Test(_, hooks)
test.state, test.setState = hooks.useState(initialState)
test.dependencies, test.setDependencies = hooks.useState(initialDependencies)

test.value = hooks.useMemo(function()
return test.state
end, test.dependencies)

return nil
end

test.handle = Roact.mount(e(Hooks.new(Roact)(Test)))
return test
end

return function()
describe("useEffect", function()
describe("when dependencies are nil", function()
it("should recalculate value after each rerender", function()
local test = createTest(1)
expect(test.value).to.equal(1)

test.setState(2)
task.wait(0.1)
expect(test.value).to.equal(2)

test.setState(3)
task.wait(0.1)
expect(test.value).to.equal(3)
end)
end)

describe("when dependencies are empty", function()
it("should not recalculate value after each rerender", function()
local test = createTest(1, {})
expect(test.value).to.equal(1)

test.setState(2)
task.wait(0.1)
expect(test.value).to.equal(1)
end)
end)

describe("when dependencies are constant", function()
it("should not recalculate value after each rerender", function()
local test = createTest(1, { 1 })
expect(test.value).to.equal(1)

test.setState(2)
task.wait(0.1)
expect(test.value).to.equal(1)
end)
end)

describe("when dependencies are changing", function()
it("should recalculate value when dependencies change", function()
local test = createTest(1, { 1 })
expect(test.value).to.equal(1)

test.setState(2)
test.setDependencies({ 2 })
task.wait(0.1)
expect(test.value).to.equal(2)

test.setState(3)
task.wait(0.1)
expect(test.value).to.equal(2)

test.setDependencies({ 3 })
task.wait(0.1)
expect(test.value).to.equal(3)
end)
end)
end)
end
53 changes: 53 additions & 0 deletions src/createUseReducer.spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Roact = require(ReplicatedStorage.Roact)
local Hooks = require(ReplicatedStorage.Hooks)

local e = Roact.createElement

local function reducer(state, action)
if action.type == "increment" then
return { count = state.count + 1 }
elseif action.type == "decrement" then
return { count = state.count - 1 }
else
error("Unknown type: " .. tostring(action.type))
end
end

local function createTest(initialState)
local test = {
renders = 0,
}

local function Test(_, hooks)
test.renders += 1
test.state, test.dispatch = hooks.useReducer(reducer, initialState)
return nil
end

test.handle = Roact.mount(e(Hooks.new(Roact)(Test)))
return test
end

return function()
describe("useReducer", function()
it("should set the initial state", function()
local test = createTest({ count = 1})
expect(test.state.count).to.equal(1)
expect(test.renders).to.equal(1)
end)

it("should rerender when the state changes", function()
local test = createTest({ count = 1})

test.dispatch({ type = "increment" })
expect(test.state.count).to.equal(2)
expect(test.renders).to.equal(2)

test.dispatch({ type = "decrement" })
expect(test.state.count).to.equal(1)
expect(test.renders).to.equal(3)
end)
end)
end
Loading