Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugins #17

Merged
merged 75 commits into from
Jun 24, 2016
Merged

Plugins #17

merged 75 commits into from
Jun 24, 2016

Conversation

searls
Copy link
Member

@searls searls commented Jun 19, 2016

This has been my hobby project for the last week. Still pretty half baked.

The tl;dr is that this PR rewrites teenytest to implement most of its user-facing logic into plugins (docs). The internal plugins are all defined here and loaded by the runner here.

This is a pretty drastic detangling of all the runner-time logic teenytest contained before, and should allow us to do things like have user-defined plugins which transform test functions like #14 with promises or a first-class reporter plugin (in lieu or in addition to the TAP13 one) as #15 wants.

  • formalize, test, and fix with greater certainty which hooks should run in which situations & states (see the reliable-hooks tests
  • define and test/implement what to do when a single scope of the test has multiple errors (basically: batch and report them all)
  • test to specify what to do when a suite-scoped plugin passes an error to its callback, as that's not actually possible with OOTB teenytest & its built-in plugins
  • make the meta-test helpers a little more rigorous. they still swallow too many errors though (requiring debugging + catch all exceptions)
  • add a test to specify timeout behavior
  • clean up all this messy code everywhere
  • make the plugin test pass
  • allow CLI users to register plugins, as currently any registered in JS would require you run teenytest using the JS API, which nobody does
    • maybe a $ teenytest --config some/requireable/path which would in turn export a function registering what it needs with teenytest, and invoked by teenytest's runner
    • maybe a package.teenytest.plugins config path where requireable paths that export plugins can be found
  • before shipping, proof-of-concept an external promise plugin
  • allow CLI users to easily unregister or move ahead/behind built-in plugins. I don't really want a new Teenytestfile type to explicitly specify all of them, but
    • it'd be nice to have control from day 1 for situations where order ends up mattering. Like append vs. prepend.
    • Or maybe the built-in modules should all have custom entry-points explicitly at the lifecycle phases so that they can't be thrown into a confusing state. this makes sense because some like results will change how subsequent wrappers get called
    • but it'd also be nice for the user to be able to un-register any plugin, particularly if they don't want TAP13

searls added 30 commits June 11, 2016 09:27
Didn't even notice these two concerns were tangled

Also disables the timeout plugin for now, because that's a huge perf hog
It's a bug that it only tests timeouts per func and not per "whole test"
The results plugin should swallow these and figure out what to do
build-test-actions: 
 * have a userFunction builder with subordinate test/hook ones
 * pass in arg that says whether they should immediately error
 * get the logging funcs out of here altogether

results plugin: 
 * userFunction immediately prints errors if the action has !belongsToTest
 * suite failures should immediately print, mark overall failing, still pop
This is totally the right place to do this.
Before any hook failures were printed above a test, even if (as in the
case of a beforeEach) they logically belong as part of the test. Now
any hook failures are printed at the test-level, right at the same time
that OK or NOT OK is printed
But it seems like it should behave sorta similar instead of being fatal

#NoTest
This bug was caught by Standard JavaScript™ c/o @feross
In general afterAll should be run even if there was a same-nesting-level
error. afterEach should be run if there was a same-nesting-level error
and it was a test-specific error
In this case, afterAll should run but not the test or any subordinate
tests. In the case of outright-skipped test functions we print "SKIPPED"
This was certainly one way to make sure the right hooks are firing when
we want. 

Particularly nasty is the use of `distanceFromTest` as a crude 
instrument to determine whether an afterEach is at a deeper depth than
an earlier error (to avoid running them unnecessarily). Just ugly as 
hell.
* Test the logging for suite-wide failures (which really ought only be 
possible if you have a plugin triggering them)
* Expose the file name for suites (and the glob itself for the top-level
suite)
Not sure how this happened, but the nesting levels introduced in 
73b4236 were counting beforeEach's completely backwards
We're effectively mucking with global-state so this will call it out and
hopefully make it addressable for use by other plugins.
Thinking about a scheme for uniquely identifying tests & suites in
order to call back the results of each
searls added 28 commits June 21, 2016 13:00
This is the first green commit in like 40 commits. Wow.
Basically I've built a teenytest to test teenytest

Great.
This required me to inline the done plugin sadly. Oh well!
(Disabling the result-value test while we refactor)

Right now the word "result" in this codebase means "test & suite 
results", but a lot of useful userFunction plugins will want to know
what the return/result value of a given userFunction was. Moreover,
some plugins might want to set their own result value for a particular 
userFunction as a means of intra-plugin communication if they register
multiple lifecycle hooks

This will require yet another "store" object, with its own statefulness,
and 4 of these managed ad hoc is just too much to not make an error at
some point, so next up we should refactor this into a common pattern.
There was way too much state floating around previously through 3 
different "store" modules, each with their own initialization and root
data structure. The potential for leaking state across multiple runs
was far too great to leave this as-was, especially given the need for
additional state to be stored.

This means that all non-functional state is always reset when the
test runner is kicked off. It also means that users will not be 
permitted to register plugins except when the runner deems it 
appropriate, which is a little bit inconvenient, but we've given users
3 ways of configuring plugins, so hopefully that's enough.
This is a little nutty, but every time a userFunction runs, its store
will persist a structure like this:

{
  value: <whatever the user func returned or calledback>
  error: <whatever the user func threw or calledback>
  plugins: {
    somePluginName: { 
      value: <whatever the somePluginName called back>
      error: <whatever the somePluginName errored>
    }
  }
}
This was enabled by ca4f6e2

In hindsight, the error store only ever existed to tangle up the user
function results with the test runner results, so now they're more
distinct
This will allow plugins to know the values set by the user's defined
functions as well as any plugins
@searls searls merged commit 237d18d into master Jun 24, 2016
@searls searls deleted the plugins branch June 24, 2016 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant