<commit_before>'use strict'

const Writable = require('stream').Writable

const express = require('express')
const path = require('path')
const assert = require('assert')

const hounds = require('../')

describe('hounds', function() {
    let server
    before(done => {
        const app = express()

        app.use(express.static(path.join(__dirname, '/fixture')))

        server = app.listen(4441, done)
    })

    after(() => {
        server.close()
    })

    beforeEach(() => {
        this.options = { url: 'http://localhost:4441/' }

        this.assertErrorReceived = () => {}

        let callCount = 0
        this.quarry = new Writable({
            objectMode: true,
            write: (chunk, enc, next) => {
                callCount++
                this.assertErrorReceived(chunk, callCount)
                next()
            }
        })
    })

    describe('returns error from a single page', () => {
        beforeEach(() => {
            this.hunt = hounds.release(this.options)
        })

        afterEach(() => {
            this.hunt.unpipe(this.quarry)
        })

        it('detects the single console error', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 1) return
                assert.equal(this.options.url, chunk.url, 'URL is passed through')
                assert.equal('Uncaught Error: Error inline script', chunk.message, 'Page error while loading is caught')
                assert.equal(3, chunk.stackTrace.length, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })

        it('detects the error after DOM loaded', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 2) return
                assert.equal(this.options.url, chunk.url, 'URL is passed through')
                assert.equal('Uncaught Error: Error after load', chunk.message, 'Page error after DOM is loaded')
                assert.equal(3, chunk.stackTrace.length, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })

        it('then it ends the stream after it finds three items', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount > 3) assert.fail(callCount, 3, 'Expected only 3 errors to have been found')
            }

            this.hunt.on('error', done).on('end', done).pipe(this.quarry)
        })
    })


    describe('when setup on an empty page', () => {
        beforeEach(() => {
            this.options.url += 'empty.html'
        })

        describe('when released', () => {
            beforeEach(() => {
                this.hunt = hounds.release(this.options)
            })

            afterEach(() => {
                this.hunt.unpipe(this.quarry)
            })

            it('then it ends the stream when complete', done => {
                this.hunt.on('error', done).on('end', done).pipe(this.quarry)
            })
        })

        describe('when keepAlive is provided and relesed', () => {
            beforeEach(() => {
                this.options.keepAlive = true
                this.hunt = hounds.release(this.options)
            })

            afterEach(() => {
                this.hunt.unpipe(this.quarry)
            })

            it('then it does NOT end the stream when complete', done => {
                this.hunt.on('error', done).on('end', () => { throw Error('The hunt was ended early!') }).pipe(this.quarry)
                setTimeout(done, 2000)
            })
        })

    })

    describe('returns error from followed page', () => {
        beforeEach(() => {
            this.hunt = hounds.release(this.options)
        })

        it('detects the console error in a followed page', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 3) return
                assert.equal(`${this.options.url}second.html`, chunk.url, 'URL is passed through')
                assert.equal('Uncaught Error: This is supposed to happen', chunk.message, 'Page error on second.html caught')
                assert.equal(3, chunk.stackTrace.length, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })
    })

    describe('when waiting on each page for 600ms after load before moving on', () => {
        beforeEach(() => {
            this.options.waitAfterLoadedFor = 600
            this.hunt = hounds.release(this.options)
        })

        afterEach(() => {
            this.hunt.unpipe(this.quarry)
        })

        it('then it detects a timed out error of 500ms', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 3) return
                assert.equal(this.options.url, chunk.url, 'URL is passed through')
                assert.equal('Uncaught Error: Error after 500ms', chunk.message, 'Page error 500ms after load is caught')
                assert.equal(3, chunk.stackTrace.length, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })
    })

    // Known bug:
    // We aren't guaranteed that the URL is still valid here,
    // as it may have changed between when this error was thrown and now.
    // A better solution is for nightmare to also emit `win.webContents.getURL()`
    // as another argument in https://github.com/segmentio/nightmare/blob/master/lib/runner.js#L115
    xdescribe('when waiting on each page for 400ms after load before moving on', () => {
        beforeEach(() => {
            this.options.waitAfterLoadedFor = 400
            this.hunt = hounds.release(this.options)
        })

        afterEach(() => {
            this.hunt.unpipe(this.quarry)
        })

        it('then it still detects the timed out error of 500ms, with the correct, previous URL', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 3) return
                assert.equal(this.options.url, chunk.url, 'URL is passed through')
                assert.equal('Uncaught Error: Error after 500ms', chunk.message, 'Page error 500ms after load is caught')
                assert.equal(3, chunk.stackTrace.length, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })
    })
})<commit_msg>Adding test for maxFollows and fixing assert orders<commit_after>'use strict'

const Writable = require('stream').Writable

const express = require('express')
const path = require('path')
const assert = require('assert')

const hounds = require('../')

describe('hounds', function() {
    let server
    before(done => {
        const app = express()

        app.use(express.static(path.join(__dirname, '/fixture')))

        server = app.listen(4441, done)
    })

    after(() => {
        server.close()
    })

    beforeEach(() => {
        this.options = { url: 'http://localhost:4441/' }

        this.assertErrorReceived = () => {}

        let callCount = 0
        this.quarry = new Writable({
            objectMode: true,
            write: (chunk, enc, next) => {
                callCount++
                this.assertErrorReceived(chunk, callCount)
                next()
            }
        })
    })

    describe('returns error from a single page', () => {
        beforeEach(() => {
            this.hunt = hounds.release(this.options)
        })

        afterEach(() => {
            this.hunt.unpipe(this.quarry)
        })

        it('detects the single console error', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 1) return
                assert.equal(chunk.url, this.options.url, 'URL is passed through')
                assert.equal(chunk.message, 'Uncaught Error: Error inline script', 'Page error while loading is caught')
                assert.equal(chunk.stackTrace.length, 3, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })

        it('detects the error after DOM loaded', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 2) return
                assert.equal(chunk.url, this.options.url, 'URL is passed through')
                assert.equal(chunk.message, 'Uncaught Error: Error after load', 'Page error after DOM is loaded')
                assert.equal(chunk.stackTrace.length, 3, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })

        it('then it ends the stream after it finds three items', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount > 3) assert.fail(callCount, 3, 'Expected only 3 errors to have been found')
            }

            this.hunt.on('error', done).on('end', done).pipe(this.quarry)
        })
    })


    describe('when setup on an empty page', () => {
        beforeEach(() => {
            this.options.url += 'empty.html'
        })

        describe('when released', () => {
            beforeEach(() => {
                this.hunt = hounds.release(this.options)
            })

            afterEach(() => {
                this.hunt.unpipe(this.quarry)
            })

            it('then it ends the stream when complete', done => {
                this.hunt.on('error', done).on('end', done).pipe(this.quarry)
            })
        })

        describe('when keepAlive is provided and relesed', () => {
            beforeEach(() => {
                this.options.keepAlive = true
                this.hunt = hounds.release(this.options)
            })

            afterEach(() => {
                this.hunt.unpipe(this.quarry)
            })

            it('then it does NOT end the stream when complete', done => {
                this.hunt.on('error', done).on('end', () => { throw Error('The hunt was ended early!') }).pipe(this.quarry)
                setTimeout(done, 2000)
            })
        })

    })

    describe('returns error from followed page', () => {
        beforeEach(() => {
            this.hunt = hounds.release(this.options)
        })

        it('detects the console error in a followed page', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 3) return
                assert.equal(chunk.url, `${this.options.url}second.html`, 'URL is passed through')
                assert.equal(chunk.message, 'Uncaught Error: This is supposed to happen', 'Page error on second.html caught')
                assert.equal(chunk.stackTrace.length, 3, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })
    })

    describe('when waiting on each page for 600ms after load before moving on', () => {
        beforeEach(() => {
            this.options.waitAfterLoadedFor = 600
            this.hunt = hounds.release(this.options)
        })

        afterEach(() => {
            this.hunt.unpipe(this.quarry)
        })

        it('then it detects a timed out error of 500ms', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 3) return
                assert.equal(chunk.url, this.options.url, 'URL is passed through')
                assert.equal(chunk.message, 'Uncaught Error: Error after 500ms', 'Page error 500ms after load is caught')
                assert.equal(chunk.stackTrace.length, 3, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })
    })

    describe('when maxFollows set to 1', () => {
        beforeEach(() => {
            this.options.maxFollows = 1
            this.hunt = hounds.release(this.options)
        })

        afterEach(() => {
            this.hunt.unpipe(this.quarry)
        })

        it('then it does not detect errors on the third page', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount <= 2) return

                if (callCount > 2) {
                    assert.fail(callCount, 2, 'Expected only 1 extra page followed, so no more than 2 errors should resolve')
                    done()
                }
            }

            this.hunt.on('error', done).on('end', done).pipe(this.quarry)
        })
    })

    // Known bug:
    // We aren't guaranteed that the URL is still valid here,
    // as it may have changed between when this error was thrown and now.
    // A better solution is for nightmare to also emit `win.webContents.getURL()`
    // as another argument in https://github.com/segmentio/nightmare/blob/master/lib/runner.js#L115
    xdescribe('when waiting on each page for 400ms after load before moving on', () => {
        beforeEach(() => {
            this.options.waitAfterLoadedFor = 400
            this.hunt = hounds.release(this.options)
        })

        afterEach(() => {
            this.hunt.unpipe(this.quarry)
        })

        it('then it still detects the timed out error of 500ms, with the correct, previous URL', done => {
            this.assertErrorReceived = (chunk, callCount) => {
                if (callCount !== 3) return
                assert.equal(chunk.url, this.options.url, 'URL is passed through')
                assert.equal(chunk.message, 'Uncaught Error: Error after 500ms', 'Page error 500ms after load is caught')
                assert.equal(chunk.stackTrace.length, 3, 'Page error stacktrace is captured')
                done()
            }

            this.hunt.on('error', done).pipe(this.quarry)
        })
    })
})
{'fraction_non_alphanumeric': 0.12959682562176716, 'fraction_numerical': 0.008857082122865444, 'mean_word_length': 1.7937450514647664, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 89, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1399669', 'n_tokens_mistral': 4007, 'n_tokens_neox': 3758, 'n_words': 994}