import * as TapReporter from '../src/tapReporter.js';

describe('The TAP reporter', function () {
    let out, reporter;

    beforeEach(function () {
        out = (function () {
            let output = '';
            return {
                print(str) {
                    output += str;
                },
                getOutput() {
                    return output;
                },
                clear() {
                    output = '';
                },
            };
        })();

        reporter = new TapReporter.TapReporter({
            print: out.print,
        });
    });

    it('outputs a test plan', function () {
        reporter.jasmineStarted({totalSpecsDefined: 1});
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'foo',
            status: 'passed',
        });
        reporter.jasmineDone();

        expect(out.getOutput()).toMatch(/1../);
    });

    it('includes the total number of specs in the test plan', function () {
        reporter.jasmineStarted({totalSpecsDefined: 2});
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'foo',
            status: 'passed',
        });
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'bar',
            status: 'passed',
        });
        reporter.jasmineDone();

        expect(out.getOutput()).toMatch(/1..2/);
    });

    it('outputs an empty test plan if there were no specs', function () {
        reporter.jasmineStarted({totalSpecsDefined: 0});
        reporter.jasmineDone();

        expect(out.getOutput()).toMatch(/1..0/);
    });

    it('outputs a line starting with "ok" for a passing spec', function () {
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'foo',
            status: 'passed',
        });
        expect(out.getOutput()).toMatch(/^ok/);
    });

    it('outputs a line starting with "not ok" for a failing spec', function () {
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'foo',
            status: 'failed',
        });
        expect(out.getOutput()).toMatch(/^not ok/);
    });

    it('outputs an "ok" line plus a skip directive for a pending spec', function () {
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'foo',
            status: 'pending',
        });
        expect(out.getOutput()).toMatch(/^ok/);
        expect(out.getOutput()).toMatch(/# skip/i);
    });

    it('reports the reason for a pending spec, if given', function () {
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'foo',
            status: 'pending',
            pendingReason: 'because I said so',
        });
        expect(out.getOutput()).toMatch('because I said so');
    });

    it('outputs an "ok" line plus a skip directive for a disabled spec', function () {
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'foo',
            status: 'disabled',
        });
        expect(out.getOutput()).toMatch(/^ok/);
        expect(out.getOutput()).toMatch(/# skip/i);
    });

    it('outputs a sequence number after the result', function () {
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'foo',
            status: 'passed',
        });
        expect(out.getOutput()).toMatch(/^ok 1/);
        out.clear();
        reporter.specStarted({});
        reporter.specDone({
            fullName: 'bar',
            status: 'failed',
        });
        expect(out.getOutput()).toMatch(/^not ok 2/);
    });

    it('outputs the name of the spec on the test line', function () {
        reporter.specStarted({});
        reporter.specDone({
            status: 'passed',
            fullName: 'A passing spec',
        });
        expect(out.getOutput()).toMatch(/^ok.*A passing spec/);
        out.clear();
        reporter.specStarted({});
        reporter.specDone({
            status: 'failed',
            fullName: 'A failing spec',
        });
        expect(out.getOutput()).toMatch(/^not ok.*A failing spec/);
    });

    it('does not let the name start with a digit', function () {
        reporter.specStarted({});
        reporter.specDone({
            status: 'passed',
            fullName: '3 careless programmers wrote this',
        });
        expect(out.getOutput()).toMatch(/^ok 1/);
        expect(out.getOutput()).not.toMatch(/^ok 1\s*[0-9]+/);
    });

    describe('on failure', function () {
        beforeEach(function () {
            reporter.specStarted({});
            out.clear();
            reporter.specDone({
                fullName: 'foo',
                status: 'failed',
                failedExpectations: [
                    {
                        message: 'Expected the earth and the sky.',
                        stack: 'line 1\nline 2\nline 3',
                    },
                    {
                        message: 'Expectations exceeded.',
                        stack: 'line 4\nline 5',
                    },
                ],
            });
        });

        it('outputs messages on the test line', function () {
            const output = out.getOutput();
            const [testLine] = output.split('\n');
            expect(testLine).toMatch(/^not ok.*Expected the earth and the sky\./);
            expect(testLine).toMatch(/^not ok.*Expectations exceeded\./);
        });

        it('outputs messages and stack traces as diagnostic lines', function () {
            const diagnostics = out.getOutput().split('\n').slice(1);
            expect(diagnostics).toMatch(/^#.*Expected the earth and the sky\./m);
            expect(diagnostics).toMatch(/^#.*Expectations exceeded\./m);
            expect(diagnostics).toMatch(/^#.*line 1/m);
            expect(diagnostics).toMatch(/^#.*line 2/m);
            expect(diagnostics).toMatch(/^#.*line 3/m);
            expect(diagnostics).toMatch(/^#.*line 4/m);
            expect(diagnostics).toMatch(/^#.*line 5/m);
        });
    });

    describe('on failure with newlines', function () {
        beforeEach(function () {
            reporter.specStarted({});
            out.clear();
            reporter.specDone({
                fullName: 'foo',
                status: 'failed',
                failedExpectations: [{
                    message: 'A message\non two lines',
                    stack: '',
                }],
            });
        });

        it('prints no newlines on the test line', function () {
            const [testLine] = out.getOutput().split('\n');
            expect(testLine).toMatch(/^not ok.*A message.*on two lines/);
        });

        it('prints no newlines in the diagnostics', function () {
            const diagnostics = out.getOutput().split('\n').slice(1);
            expect(diagnostics).not.toMatch(/^[^#]/);
        });
    });

    it('reports suites starting as diagnostic lines', function () {
        reporter.suiteStarted({
            fullName: 'A suite',
        });
        expect(out.getOutput()).toMatch(/^#.*A suite/);
    });

    it('reports suites finishing successfully as diagnostic lines', function () {
        reporter.suiteDone({
            status: 'passed',
            fullName: 'A suite',
            failedExpectations: [],
        });
        expect(out.getOutput()).toMatch(/^#.*A suite/);
    });

    it('reports suites with failing specs as diagnostic lines', function () {
        reporter.suiteDone({
            status: 'failed',
            fullName: 'A suite',
            failedExpectations: [],
        });
        expect(out.getOutput()).toMatch(/^#.*A suite/);
    });

    it('reports failures in afterAll as an extra failure', function () {
        reporter.suiteDone({
            status: 'failed',
            fullName: 'A suite',
            failedExpectations: [{message: 'An afterAll exception'}],
        });
        reporter.jasmineDone();
        expect(out.getOutput()).toMatch(/^not ok.*An afterAll exception/m);
    });
});
