Skip to content
This repository was archived by the owner on Sep 14, 2024. It is now read-only.

Add expect.extend #142

Merged
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
* Limitations:
* `expect.extend` cannot be called from within `describe` blocks
* Custom matcher names cannot overwrite pre-existing matchers, including default matchers and matchers introduces from previous `expect.extend` calls.
* Custom matcher names may not start with "_"

## 0.3.3 (2020-09-25)
* Remove the lifecycle hooks from the session tree. This prevents the `[?]` spam from the reporter not recognizing these nodes.
Expand Down
19 changes: 13 additions & 6 deletions src/Expectation.lua
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ function Expectation.new(value)
local self = {
value = value,
successCondition = true,
condition = false
condition = false,
matchers = {},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is matchers used? I don't see it anywhere else.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We use it once when we need to negate our expectation
https://github.com/Roblox/testez/pull/142/files#diff-c51d01b32afee27efe59f26103c1e704R129

We pass in the non-bound versions of the extend config.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh, yep. There it is.

_boundMatchers = {},
}

setmetatable(self, Expectation)
Expand All @@ -91,8 +93,8 @@ function Expectation.new(value)
return self
end

function Expectation:checkMatcherNameCollisions(name)
if SELF_KEYS[name] or NEGATION_KEYS[name] or self[name] then
function Expectation.checkMatcherNameCollisions(name)
if SELF_KEYS[name] or NEGATION_KEYS[name] or Expectation[name] then
return false
end

Expand All @@ -103,9 +105,7 @@ function Expectation:extend(matchers)
self.matchers = matchers or {}

for name, implementation in pairs(self.matchers) do
assert(name:sub(1, 1) ~= "_", string.format("Cannot write matcher %q. Matchers cannot start with %q", name, "_"))
assert(self:checkMatcherNameCollisions(name), string.format("Cannot overwrite matcher %q; it already exists", name))
self[name] = bindSelf(self, function(_self, ...)
self._boundMatchers[name] = bindSelf(self, function(_self, ...)
local result = implementation(self.value, ...)
local pass = result.pass == self.successCondition

Expand All @@ -132,6 +132,10 @@ function Expectation.__index(self, key)
return newExpectation
end

if self._boundMatchers[key] then
return self._boundMatchers[key]
end

-- Fall back to methods provided by Expectation
return Expectation[key]
end
Expand Down Expand Up @@ -178,6 +182,9 @@ function Expectation:a(typeName)
return self
end

-- Make alias public on lass
Expectation.an = Expectation.a

--[[
Assert that our expectation value is truthy
]]
Expand Down
6 changes: 3 additions & 3 deletions src/ExpectationContext.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
local Expectation = require(script.Parent.Expectation)
local checkMatcherNameCollisions = Expectation.checkMatcherNameCollisions

local function copy(t)
local result = {}
Expand Down Expand Up @@ -27,9 +28,8 @@ end

function ExpectationContext:extend(config)
for key, value in pairs(config) do
if self._extensions[key] then
error(string.format("Cannot reassign %q in expect.extend", key))
end
assert(self._extensions[key] == nil, string.format("Cannot reassign %q in expect.extend", key))
assert(checkMatcherNameCollisions(key), string.format("Cannot overwrite matcher %q; it already exists", key))

self._extensions[key] = value
end
Expand Down
2 changes: 1 addition & 1 deletion src/TestPlan.lua
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ local function newEnvironment(currentNode, extraEnvironment)

env.expect = setmetatable({
extend = function(...)
error(string.format("Cannot call %q from within a %q node.", "expect.extend", "describe"))
error("Cannot call \"expect.extend\" from within a \"describe\" node.")
end,
}, {
__call = function(_self, ...)
Expand Down
68 changes: 27 additions & 41 deletions tests/passing/expectExtend-illegalWrites.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,61 +8,47 @@ local noOptMatcher = function(_received, _expected)
end

local ERROR_CANNOT_OVERWRITE = "Cannot overwrite matcher"
local ERROR_CANNOT_START_WITH = "Matchers cannot start with"

local runTest = function(expectedError)
return function()
local success, message = pcall(function()
expect()
end)

assert(success == false, "should have been thrown")
assert(message:match(expectedError), string.format("\nUnexpected error:\n%s", message))
end
end

return function()
describe("attempt to overwrite default", function()
beforeAll(function()
expect.extend({
-- This should throw since `ok` is a default matcher
ok = noOptMatcher,
})
local success, message = pcall(function()
expect.extend({
-- This should throw since `ok` is a default matcher
ok = noOptMatcher,
})
end)

assert(success == false, "should have thrown")
assert(message:match(ERROR_CANNOT_OVERWRITE), string.format("\nUnexpected error:\n%s", message))
end)

it("SHOULD fail with expected error message", runTest(ERROR_CANNOT_OVERWRITE))
end)

describe("attempt to overwrite never", function()
beforeAll(function()
expect.extend({
-- This should throw since `never` is protected
never = noOptMatcher,
})
local success, message = pcall(function()
expect.extend({
-- This should throw since `never` is a default matcher
never = noOptMatcher,
})
end)

assert(success == false, "should have thrown")
assert(message:match(ERROR_CANNOT_OVERWRITE), string.format("\nUnexpected error:\n%s", message))
end)

it("SHOULD fail with expected error message", runTest(ERROR_CANNOT_OVERWRITE))
end)

describe("attempt to overwrite self", function()
beforeAll(function()
expect.extend({
-- This should throw since `a` is protected
a = noOptMatcher,
})
local success, message = pcall(function()
expect.extend({
-- This should throw since `a` is a default matcher
a = noOptMatcher,
})
end)

assert(success == false, "should have thrown")
assert(message:match(ERROR_CANNOT_OVERWRITE), string.format("\nUnexpected error:\n%s", message))
end)

it("SHOULD fail with expected error message", runTest(ERROR_CANNOT_OVERWRITE))
end)

describe("attempt to start with _", function()
beforeAll(function()
expect.extend({
-- This should throw since this starts with _
_fooBar = noOptMatcher,
})
end)

it("SHOULD fail with expected error message", runTest(ERROR_CANNOT_START_WITH))
end)
end