dtao / autodoc

Doc generation on steroids
https://npmjs.org/package/autodoc
MIT License
232 stars 15 forks source link

(Custom) Error Messages in Custom Handlers #58

Open deje1011 opened 8 years ago

deje1011 commented 8 years ago

First of all, thanks a lot for this project! I love the idea of having testable examples right on top of your functions.

I tried to get it to work with promises and I was surprised how easy it is to integrate new features like that (although it took me a while to get started, I guess the documentation could be a little clearer about how to write and use custom handlers). Anyway, thats what I came up with in order to test functions that return promises:

(Btw, just in case somebody struggles getting started with custom handlers as well, here is my package.json scripts configuration):

"scripts": {
    "test": "autodoc --test --handlers autodoc-helpers/autodoc-helpers.js --partials ./autodoc-helpers --verbose ./my-file-name.js",
    "docs": "autodoc --handlers ./autodoc-helpers/autodoc-helpers.js --partials ./autodoc-helpers ./my-file-name.js"
}

autodoc-helpers/autodoc-helpers.js:

this.exampleHandlers = [
    {
        pattern: /^resolves in (.*)$/,
        template: 'autodoc-promise-resolve',
        data: function(match) {
            return { expectedResult : match[1] };
        }
    },
    {
        pattern: /^rejects with (.*)$/,
        template: 'autodoc-promise-reject',
        data: function(match) {
            return { expectedError : match[1] };
        }
    }
];

autodoc-helpers/autodoc-promise-resolve.js:

var test = {
    error : undefined,
    result : undefined,
    onSuccessCallCount : 0,
    onErrorCallCount : 0
};
var onSuccess = function (result) {
    test.result = result;
    test.onSuccessCallCount++;
};
var onError = function (error) {
    test.error = error;
    test.onErrorCallCount++;
};

runs(function() {
    {{{actual}}}.then(onSuccess).catch(onError);
});

waitsFor(function() {
    return test.onSuccessCallCount > 0 || test.onErrorCallCount > 0;
});

runs(function() {
    expect(test.onErrorCallCount).toEqual(0);
    expect(test.onSuccessCallCount).toEqual(1);
    expect(test.result).toEqual({{{expectedResult}}});
});

autodoc-helpers/autodoc-promise-reject.js:


var test = {
    error : undefined,
    result : undefined,
    onSuccessCallCount : 0,
    onErrorCallCount : 0
};
var onSuccess = function (result) {
    test.result = result;
    test.onSuccessCallCount++;
};
var onError = function (error) {
    test.error = error;
    test.onErrorCallCount++;
};

runs(function() {
    {{{actual}}}.then(onSuccess).catch(onError);
});

waitsFor(function() {
    return test.onSuccessCallCount > 0 || test.onErrorCallCount > 0;
});

runs(function() {
    expect(test.onSuccessCallCount).toEqual(0);
    expect(test.onErrorCallCount).toEqual(1);
    expect(test.error).toEqual({{{expectedError}}});
});

Now, I can write tests like this:

/**
    @example
    promiseFn() // resolves in {foo : 'bar'}
*/
function promiseFn () {
    return Promise.resolve({foo : 'bar'});
}

module.exports = promiseFn;

I had to export the function that I wanted to test, otherwise I would get "promiseFn is not defined". So my first question is: Does that mean that you can only test functions that you make public by either exporting them directly or wrapped inside an exported object?

For the case that everything works fine, this works great! But if the expected result varies from the actual output, I get a weird error message (saying "Passed" in red letters). But at least it tells me that the test failed.

/**
    @example
    promiseFn() // resolves in {foo : 'baz'}
*/
function promiseFn () {
    return Promise.resolve({foo : 'bar'});
}

module.exports = promiseFn;

console output:


F

  1) Passed.

    14: /**
    15:     @example
    16:     promiseFn() // rejects with {foo : 'baz'}
    17: */
    18: function promiseFn () {
    19:     return Promise.reject({foo : 'bar'});
    20: }

  Ran 1 specs in 24ms.
  0 passed, 1 failed

So my second and main question is: How do I get autodoc to display an error message that makes sense?

And one last thing:

While testing the error cases of my functions, I noticed that it is quite easy to do as long I know exactly what the error looks like:

/**
    @example
    promiseFn() // rejects with {errorMessage : 'Static text', code : 42}
*/
function promiseFn () {
    return Promise.reject({errorMessage : 'Static text', code : 42});
}

module.exports = promiseFn;

But as soon as the error is dynamic, I had to do some weird unpacking which seems a little unintuitive to me.

/**
    @example
    var extractErrorCode = function (error) {
        return Promise.reject(error.code);
    };
    promiseFn().catch(extractErrorCode) // rejects with 42
*/
function promiseFn () {
    return Promise.reject({errorMessage : 'Dynamic text', code : 42});
}

module.exports = promiseFn;

Does anyone have an idea how to solve this more elegantly?

One last question: Is this project still active and do you still think it is a good idea to write tests and documentation in this way?

Looking forward to your response, Jesse