diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cb2f3d..d86dd8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ * `beforeAll` now runs on entering the block, rather than on the first `it` encountered after entering the block. The major difference for the moment is that a `beforeAll` will now run even if there are no `it` blocks under it, which is now consistent with how `afterAll` worked. * `beforeAll` and `afterAll` now report errors by creating a dummy node in the results to contain the error. Previously, errors in `afterAll` were not reported. * A failure in a `beforeAll` block will now halt all further test execution within its enclosing `describe` block except for any remaining `beforeAll` blocks and any `afterAll` blocks. Multiple `beforeAll` or `afterAll` blocks within one `describe` block should not count on running in any specific order. `afterAll` blocks should account for the possibility of a partially setup state when cleaning up. +* Add a context object visible from lifecycle hooks and `it` blocks. This is a write-once store for whatever you need to communicate between hooks and tests. It can be ignored until you need it. + * In particular, you can usually just use upvalues to comminucate between hooks and tests, but that won't work if your hooks are in a separate file (e.g. `init.spec.lua`). + * Also, this provides a cleaner alternative to extraEnvironment for passing along helper functions to large numbers of tests as the context can be scoped to particular directories as needed. ## 0.3.0 (2020-06-12) * Remove the `try` node type. diff --git a/src/TestRunner.lua b/src/TestRunner.lua index 1ef48bb..c3f4467 100644 --- a/src/TestRunner.lua +++ b/src/TestRunner.lua @@ -70,9 +70,16 @@ function TestRunner.runPlanNode(session, planNode, lifecycleHooks) errorMessage = messagePrefix .. message .. "\n" .. debug.traceback() end - local nodeSuccess, nodeResult = xpcall(callback, function(message) - return messagePrefix .. message .. "\n" .. debug.traceback() - end) + local context = session:getContext() + + local nodeSuccess, nodeResult = xpcall( + function() + callback(context) + end, + function(message) + return messagePrefix .. message .. "\n" .. debug.traceback() + end + ) -- If a node threw an error, we prefer to use that message over -- one created by fail() if it was set. diff --git a/src/TestSession.lua b/src/TestSession.lua index 333444a..1094285 100644 --- a/src/TestSession.lua +++ b/src/TestSession.lua @@ -9,6 +9,7 @@ local TestEnum = require(script.Parent.TestEnum) local TestResults = require(script.Parent.TestResults) +local Context = require(script.Parent.Context) local TestSession = {} @@ -23,6 +24,7 @@ function TestSession.new(plan) local self = { results = TestResults.new(plan), nodeStack = {}, + contextStack = {}, hasFocusNodes = false } @@ -95,11 +97,13 @@ end ]] function TestSession:pushNode(planNode) local node = TestResults.createNode(planNode) - local lastNode = self.nodeStack[#self.nodeStack] or self.results + local lastContext = self.contextStack[#self.contextStack] + local context = Context.new(lastContext) table.insert(lastNode.children, node) table.insert(self.nodeStack, node) + table.insert(self.contextStack, context) end --[[ @@ -108,6 +112,15 @@ end function TestSession:popNode() assert(#self.nodeStack > 0, "Tried to pop from an empty node stack!") table.remove(self.nodeStack, #self.nodeStack) + table.remove(self.contextStack, #self.contextStack) +end + +--[[ + Gets the Context object for the current node. +]] +function TestSession:getContext() + assert(#self.contextStack > 0, "Tried to get context from an empty stack!") + return self.contextStack[#self.contextStack] end --[[ @@ -172,7 +185,9 @@ function TestSession:setError(message) end --[[ - Add a dummy node below the current one to hold an error message. + Add a dummy child node to the current node to hold the given error. This + allows an otherwise empty describe node to report an error in a more natural + way. ]] function TestSession:addDummyError(phrase, message) self:pushNode({type = TestEnum.NodeType.It, phrase = phrase}) diff --git a/tests/passing/context.spec.lua b/tests/passing/context.spec.lua new file mode 100644 index 0000000..c548154 --- /dev/null +++ b/tests/passing/context.spec.lua @@ -0,0 +1,27 @@ +-- luacheck: globals describe beforeAll beforeEach it expect afterEach afterAll + +return function() + describe("context is passed between lifecycle hooks and it blocks", function() + beforeAll(function(context) + context.a = 1 + end) + + beforeEach(function(context) + context.b = 1 + end) + + it("before hooks should run", function(context) + expect(context.a).to.equal(1) + expect(context.b).to.equal(1) + end) + + afterEach(function(context) + expect(context.b).to.equal(1) + end) + + afterAll(function(context) + -- Failures in afterAll aren't reported. + expect(context.a).to.equal(1) + end) + end) +end \ No newline at end of file diff --git a/tests/passing/describeAndContext.spec.lua b/tests/passing/describeAndContext.spec.lua new file mode 100644 index 0000000..5c6b251 --- /dev/null +++ b/tests/passing/describeAndContext.spec.lua @@ -0,0 +1,7 @@ +-- luacheck: globals describe expect + +return function() + describe("this shouldn't be able to access context", function(context) + expect(context).to.never.be.ok() + end) +end diff --git a/tests/passing/nestedContext.spec.lua b/tests/passing/nestedContext.spec.lua new file mode 100644 index 0000000..e5758f0 --- /dev/null +++ b/tests/passing/nestedContext.spec.lua @@ -0,0 +1,25 @@ +-- luacheck: globals describe beforeAll it expect + +return function() + describe("setting context here", function() + beforeAll(function(context) + context.a = 1 + end) + + describe("should apply here", function() + beforeAll(function(context) + context.b = context.a + 1 + end) + + it("should see a and b", function(context) + expect(context.a).to.equal(1) + expect(context.b).to.equal(2) + end) + end) + + it("should not see b here", function(context) + expect(context.a).to.equal(1) + expect(context.b).to.never.be.ok() + end) + end) +end \ No newline at end of file