diff --git a/.gitignore b/.gitignore index ac2fb8c..c168120 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # Dependencies /docbox/** modules/** +tests/testbox/** +testbox/** # Build Artifacts .tmp/** diff --git a/box.json b/box.json index f0e9c4a..6293479 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"TestBox CLI", - "version":"1.1.3", + "version":"1.2.0", "location":"https://downloads.ortussolutions.com/ortussolutions/commandbox-modules/testbox-cli/@build.version@/testbox-cli-@build.version@.zip", "slug":"testbox-cli", "author":"Ortus Solutions, Corp", @@ -25,11 +25,16 @@ "commandbox-cfformat":"*", "commandbox-docbox":"*" }, - "dependencies":{}, - "installPaths":{}, + "dependencies":{ + "testbox":"^5.3.0+5" + }, + "installPaths":{ + "testbox":"testbox/" + }, "ignore":[ "**/.*", - "build/*" + "build/*", + "tests" ], "scripts":{ "build:module":"task run taskFile=build/Build.cfc :projectName=`package show slug` :version=`package show version`", diff --git a/changelog.md b/changelog.md index 8f535ba..3dead11 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- If a localized version of TestBox cannot be found, then it installs one for you + +### Fixed + +- More fixes on runner not working +- Localized `testbox` location finding for `outputFormats` was using the wrong path + ## [1.1.3] - 2023-08-16 ### Fixed diff --git a/commands/testbox/run.cfc b/commands/testbox/run.cfc index f945009..74c8477 100644 --- a/commands/testbox/run.cfc +++ b/commands/testbox/run.cfc @@ -94,7 +94,7 @@ component { * @outputFile We will store the results in this output file as well as presenting it to you. * @outputFormats A list of output reporter to produce using the runner's JSON results only. Available formats are: json,xml,junit,antjunit,simple,dot,doc,min,mintext,doc,text,tap,codexwiki * @verbose Display extra details including passing and skipped tests. - * @testboxUseLocal When using outputformats, prefer testbox installation in current working directory over bundled version. + * @testboxUseLocal When using outputformats, prefer testbox installation in current working directory over bundled version. If none found, it tries to download one **/ function run( string runner = "", @@ -111,7 +111,7 @@ component { string outputFile, string outputFormats = "", boolean verbose, - boolean testboxUseLocal = false + boolean testboxUseLocal = true ){ // Ensure TestBox For reporting and conversions ensureTestBox( arguments.testboxUseLocal ); @@ -417,22 +417,37 @@ component { /** * Ensure that TestBox is installed + * + * @testboxUseLocal Use a local version of TestBox or in the execution path. Defaults to true, else it tries to download it */ - private function ensureTestBox( boolean testboxUseLocal = false ){ - if ( testboxUseLocal ) { - var targetPath = resolvePath( "testbox" ); - if ( !directoryExists( targetPath ) ) { - error( - "Uh-oh, you've asked to use a local copy of TestBox, but [#targetPath#] doesn't exist.", - "Do you need to run [box install]?" - ); - } - } else { - var targetPath = variables.moduleConfig.path & "/testbox"; + private function ensureTestBox( boolean testboxUseLocal = true ){ + // Where it should go in module + var testBoxPath = variables.moduleConfig.path & "/testbox"; + var modulePath = variables.moduleConfig.path; + + // Check if installed locally + if ( arguments.testboxUseLocal ) { + testBoxPath = resolvePath( "testbox" ); + } + + if ( !directoryExists( testBoxPath ) ) { + variables.print + .blackOnWheat1( " WARN " ) + .line( " Uh-oh, TestBox could not be found locally [#testBoxPath#] or in the CLI path." ) + .green1onDodgerBlue2( " INFO " ) + .line( " We will install a local version in the CLI path [#modulePath#] for you." ) + .line() + .toConsole(); + + command( "install" ).params( "testbox", modulePath ).run(); } // Add our mapping - variables.fileSystemUtil.createMapping( "/testbox", targetPath ); + variables.fileSystemUtil.createMapping( "/testbox", testBoxPath ); + // variables.print + // .green1onDodgerBlue2( " INFO " ) + // .line( " Created [/testbox] mapping at [#testBoxPath#]" ) + // .toConsole(); } } diff --git a/tests/Application.cfc b/tests/Application.cfc new file mode 100644 index 0000000..d8d5808 --- /dev/null +++ b/tests/Application.cfc @@ -0,0 +1,22 @@ +/** + * Copyright Since 2005 Ortus Solutions, Corp + * www.ortussolutions.com + * ************************************************************************************* + */ +component { + + this.name = "A TestBox Runner Suite " & hash( getCurrentTemplatePath() ); + // any other application.cfc stuff goes below: + this.sessionManagement = true; + + // any mappings go here, we create one that points to the root called test. + this.mappings[ "/tests" ] = getDirectoryFromPath( getCurrentTemplatePath() ); + + // any orm definitions go here. + + // request start + public boolean function onRequestStart( String targetPage ){ + return true; + } + +} diff --git a/tests/box.json b/tests/box.json new file mode 100644 index 0000000..8aa62c6 --- /dev/null +++ b/tests/box.json @@ -0,0 +1,61 @@ +{ + "author":"", + "bugs":"", + "changelog":"", + "contributors":[], + "dependencies":{ + "testbox":"^5.3.0+5" + }, + "description":"", + "devDependencies":{}, + "documentation":"", + "homepage":"", + "ignore":[ + "**/.*", + "/test/", + "/tests/" + ], + "installPaths":{ + "testbox":"testbox/" + }, + "instructions":"", + "keywords":[], + "license":[ + { + "type":"", + "URL":"" + } + ], + "location":"ForgeboxStorage", + "name":"My Package", + "private":false, + "projectURL":"", + "reinitWatchDelay":500, + "reinitWatchDirectory":"", + "reinitWatchPaths":"", + "repository":{ + "type":"", + "URL":"" + }, + "scripts":{}, + "shortDescription":"A sweet package", + "slug":"my-package", + "testbox":{ + "bundles":"", + "directory":"tests.specs", + "excludes":"", + "labels":"", + "options":{}, + "recurse":true, + "reporter":"", + "runner":"http://127.0.0.1:60378/runner.cfm", + "testBundles":"", + "testSpecs":"", + "testSuites":"", + "verbose":true, + "watchDelay":500, + "watchPaths":"**.cfc" + }, + "type":"modules", + "version":"0.0.0" +} diff --git a/tests/runner.cfm b/tests/runner.cfm new file mode 100644 index 0000000..b975a47 --- /dev/null +++ b/tests/runner.cfm @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/specs/BDDTest.cfc b/tests/specs/BDDTest.cfc new file mode 100644 index 0000000..af32a6c --- /dev/null +++ b/tests/specs/BDDTest.cfc @@ -0,0 +1,117 @@ +/** + * My first spec file + */ +component extends="testbox.system.BaseSpec" { + + /*********************************** LIFE CYCLE Methods ***********************************/ + + function beforeAll(){ + // setup the entire test bundle here + variables.salvador = 1; + } + + function afterAll(){ + // do cleanup here + } + + /*********************************** BDD SUITES ***********************************/ + + function run(){ + /** + * describe() starts a suite group of spec tests. It is the main BDD construct. + * You can also use the aliases: story(), feature(), scenario(), given(), when() + * to create fluent chains of human-readable expressions. + * + * Arguments: + * + * @title Required: The title of the suite, Usually how you want to name the desired behavior + * @body Required: A closure that will resemble the tests to execute. + * @labels The list or array of labels this suite group belongs to + * @asyncAll If you want to parallelize the execution of the defined specs in this suite group. + * @skip A flag that tells TestBox to skip this suite group from testing if true + * @focused A flag that tells TestBox to only run this suite and no other + */ + describe( "A spec", () => { + /** + * -------------------------------------------------------------------------- + * Runs before each spec in THIS suite group or nested groups + * -------------------------------------------------------------------------- + */ + beforeEach( () => { + testbox = 0; + testbox++; + } ); + + /** + * -------------------------------------------------------------------------- + * Runs after each spec in THIS suite group or nested groups + * -------------------------------------------------------------------------- + */ + afterEach( () => { + foo = 0; + } ); + + /** + * it() describes a spec to test. Usually the title is prefixed with the suite name to create an expression. + * You can also use the aliases: then() to create fluent chains of human-readable expressions. + * + * Arguments: + * + * @title The title of this spec + * @body The closure that represents the test + * @labels The list or array of labels this spec belongs to + * @skip A flag or a closure that tells TestBox to skip this spec test from testing if true. If this is a closure it must return boolean. + * @data A struct of data you would like to bind into the spec so it can be later passed into the executing body function + * @focused A flag that tells TestBox to only run this spec and no other + */ + it( "can test for equality", () => { + expect( testbox ).toBe( 1 ); + } ); + + it( "can have more than one expectation to test", () => { + testbox = testbox * 8; + // type checks + expect( testbox ).toBeTypeOf( "numeric" ); + // dynamic type methods + expect( testbox ).toBeNumeric(); + // delta ranges + expect( testbox ).toBeCloseTo( expected = 10, delta = 2 ); + } ); + + it( "can have negative expectations", () => { + testbox = testbox * 8; + // type checks + expect( testbox ).notToBeTypeOf( "usdate" ); + // dynamic type methods + expect( testbox ).notToBeArray(); + // delta ranges + expect( testbox ).notToBeCloseTo( expected = 10, delta = 2 ); + } ); + + xit( "can have tests that can be skipped easily like this one by prefixing it with x", () => { + fail( "xit() this should skip" ); + } ); + + it( + title = "can have tests that execute if the right environment exists (lucee only)", + body = () => { + expect( server ).toHaveKey( "lucee" ); + }, + skip = ( !isLucee() ) + ); + + it( + title = "can have tests that execute if the right environment exists (Adobe only)", + body = () => { + expect( server ).notToHaveKey( "lucee" ); + }, + skip = ( isLucee() ) + ); + } ); + } + + private function isLucee(){ + return ( structKeyExists( server, "lucee" ) ); + } + +}