/** * Sets the stub logic. * * @param mixed $substitute The logic. */ public function toBeOK() { if (!is_string($this->_actual)) { throw new Exception("Error `toBeOK()` need to be applied on a fully-namespaced class or function name."); } if ($this->_isClass) { Monkey::patch($this->_actual, Double::classname()); } else { Monkey::patch($this->_actual, function () { }); } }
} }); it("creates a collection of existing entities", function () { $model = $this->model; $data = [['id' => '1', 'title' => 'Amiga 1200'], ['id' => '2', 'title' => 'Las Vegas']]; $collection = $model::create($data, ['type' => 'set', 'exists' => true]); expect($collection)->toBeAnInstanceOf(Collection::class); expect($collection->data())->toBe($data); foreach ($collection as $entity) { expect($entity)->toBeAnInstanceOf($model); expect($entity->exists())->toBe(true); } }); it("creates an entity of a different class", function () { $model = $this->model; $subclass = Double::classname(['extends' => $model]); $entity = $model::create([], ['model' => $subclass]); expect($entity)->toBeAnInstanceOf($subclass); }); }); describe("::query()", function () { it("gets/sets the default query parameters", function () { $model = $this->model; $model::query(['field' => 'value']); expect($model::query())->toBe(['field' => 'value']); $model::query([]); }); }); describe("::validator()", function () { it("gets/sets a validator", function () { $validator = Double::instance();
Matcher::reset(); foreach ($this->matchers as $name => $value) { foreach ($value as $for => $class) { Matcher::register($name, $class, $for); } } }); describe("->__call()", function () { it("throws an exception when using an undefined matcher name", function () { $closure = function () { $result = expectation(true)->toHelloWorld(true); }; expect($closure)->toThrow(new Exception("Unexisting matcher attached to `'toHelloWorld'`.")); }); it("throws an exception when a specific class matcher doesn't match", function () { Matcher::register('toEqualCustom', Double::classname(['extends' => 'Kahlan\\Matcher\\ToEqual']), 'stdClass'); $closure = function () { $result = expectation([])->toEqualCustom(new stdClass()); }; expect($closure)->toThrow(new Exception("Unexisting matcher attached to `'toEqualCustom'` for `stdClass`.")); }); it("doesn't wait when the spec passes", function () { $start = microtime(true); $result = expectation(true, 1)->toBe(true); $end = microtime(true); expect($end - $start)->toBeLessThan(1); }); it("loops until the timeout is reached on failure", function () { $start = microtime(true); $result = expectation(true, 0.1)->toBe(false); $end = microtime(true);
$class = $this->class; $subclass = Double::classname(['extends' => $class]); allow($subclass)->toReceive('::filterable')->andRun(function () { return Filters::run(get_called_class(), 'filterable', func_get_args(), function ($next, $message) { return "Hello {$message}"; }); }); Filters::apply($class, 'filterable', $this->filter1); Filters::apply($subclass, 'filterable', $this->filter2); expect($subclass::filterable('World!'))->toBe('21Hello World!12'); Filters::apply($subclass, 'filterable', $this->noChain); expect($subclass::filterable('World!'))->toBe("Hello"); }); it("applies filters in order", function () { $class = $this->class; $subclass = Double::classname(['extends' => $class]); allow($subclass)->toReceive('::filterable')->andRun(function () { return Filters::run(get_called_class(), 'filterable', func_get_args(), function ($next, $message) { return "Hello {$message}"; }); }); Filters::apply($subclass, 'filterable', $this->filter1); Filters::apply($subclass, 'filterable', $this->filter2); expect($subclass::filterable('World!'))->toBe('21Hello World!12'); }); }); describe("::get()", function () { it("exports filters setted as a class level", function () { Filters::apply($this->class, 'filterable', $this->filter1); $filters = Filters::get(); expect($filters)->toHaveLength(1);
it("throws an exception when using an undefined matcher name", function () { $closure = function () { Matcher::get('toHelloWorld'); }; expect($closure)->toThrow(new Exception("Unexisting default matcher attached to `'toHelloWorld'`.")); }); it("throws an exception when using an undefined matcher name for a specific class", function () { $closure = function () { Matcher::get('toHelloWorld', 'stdClass'); }; expect($closure)->toThrow(new Exception("Unexisting matcher attached to `'toHelloWorld'` for `stdClass`.")); }); }); describe("::unregister()", function () { it("unregisters a matcher", function () { Matcher::register('toBeOrNotToBe', Double::classname(['extends' => 'Kahlan\\Matcher\\ToBe'])); expect(Matcher::exists('toBeOrNotToBe'))->toBe(true); Matcher::unregister('toBeOrNotToBe'); expect(Matcher::exists('toBeOrNotToBe'))->toBe(false); }); it("unregisters all matchers", function () { expect(Matcher::get())->toBeGreaterThan(1); Matcher::unregister(true); Matcher::register('toHaveLength', 'Kahlan\\Matcher\\ToHaveLength'); expect(Matcher::get())->toHaveLength(1); }); }); describe("::reset()", function () { it("unregisters all matchers", function () { expect(Matcher::get())->toBeGreaterThan(1); Matcher::reset();
$collection->data([]); }); }); describe("::toArray()", function () { it("converts a collection to an array", function () { $collection = new Collection(['data' => [1, 2, 3, 4, 5]]); expect(Collection::toArray($collection))->toBe([1, 2, 3, 4, 5]); }); it("converts objects which support __toString", function () { $stringable = Double::classname(); allow($stringable)->toReceive('__toString')->andReturn('hello'); $collection = new Collection(['data' => [new $stringable()]]); expect(Collection::toArray($collection))->toBe(['hello']); }); it("converts objects using handlers", function () { $handlable = Double::classname(); $handlers = [$handlable => function ($value) { return 'world'; }]; $collection = new Collection(['data' => [new $handlable()]]); expect(Collection::toArray($collection, compact('handlers')))->toBe(['world']); }); it("doesn't convert unsupported objects", function () { $collection = new Collection(['data' => [(object) 'an object']]); expect(Collection::toArray($collection))->toEqual([(object) 'an object']); }); it("converts nested collections", function () { $collection = new Collection(['data' => [1, 2, 3, new Collection(['data' => [4, 5, 6]])]]); expect(Collection::toArray($collection))->toBe([1, 2, 3, [4, 5, 6]]); }); it("converts mixed nested collections & arrays", function () {
$backtrace = Debugger::trace(['trace' => new Exception('World Destruction Error!')]); expect($backtrace)->toBeA('string'); $backtrace = explode("\n", $backtrace); expect(empty($backtrace))->toBe(false); }); it("returns a trace from eval'd code", function () { $trace = debug_backtrace(); $trace[1]['file'] = "eval()'d code"; $backtrace = Debugger::trace(['trace' => $trace]); expect($backtrace)->toBeA('string'); $trace = current(explode("\n", $backtrace)); expect($trace)->toMatch('~src[/|\\\\]Specification.php~'); }); describe("::_line()", function () { beforeEach(function () { $this->debugger = Double::classname(['extends' => 'Kahlan\\Analysis\\Debugger', 'methods' => ['::line']]); allow($this->debugger)->toReceive('::line')->andRun(function ($trace) { return static::_line($trace); }); }); it("returns `null` with non-existing files", function () { $debugger = $this->debugger; $trace = ['file' => DS . 'some' . DS . 'none' . DS . 'existant' . DS . 'path' . DS . 'file.php', 'line' => null]; expect($debugger::line($trace))->toBe(null); }); it("returns `null` when a line can't be found", function () { $debugger = $this->debugger; $nbline = count(file('spec' . DS . 'Suite' . DS . 'Analysis' . DS . 'Debugger.spec.php')) + 1; $trace = ['file' => 'spec' . DS . 'Suite' . DS . 'Analysis' . DS . 'Debugger.spec.php', 'line' => $nbline + 1]; expect($debugger::line($trace))->toBe(null); });
it("gets a value using a virtual field", function () { $model = $this->model; $schema = $model::definition(); $schema->column('hello_boy', ['getter' => function ($entity, $data, $name) { return 'Hi Boy!'; }]); $entity = $model::create(); expect($entity->hello_boy)->toBe('Hi Boy!'); }); context("when a model is defined", function () { beforeEach(function () { $this->model = Double::classname(['extends' => $this->model]); }); it("autoboxes setted data", function () { $model = $this->model; $childEntity = Double::classname(['extends' => $this->model]); $childEntity::definition()->locked(false); $schema = new Schema(['model' => $model]); $schema->column('child', ['type' => 'object', 'model' => $childEntity]); $model::definition($schema); $entity = $model::create(); $entity['child'] = ['id' => 1, 'title' => 'child record', 'enabled' => true]; $child = $entity['child']; expect($child)->toBeAnInstanceOf($childEntity); expect($child->parents()->get($entity))->toBe('child'); expect($child->basePath())->toBe('child'); }); }); }); describe("->validates()", function () { beforeEach(function () {
/** * Stubs a method. * * @param string $path Method name or array of stubs where key are method names and * values the stubs. * @param string $closure The stub implementation. * @return Method[] The created array of method instances. * @return Method The stubbed method instance. */ public function method($path, $closure = null) { if ($this->_needToBePatched) { $layer = Double::classname(); Monkey::patch($this->_reference, $layer); $this->_needToBePatched = false; $this->_reference = $layer; } $reference = $this->_reference; if (!$path) { throw new InvalidArgumentException("Method name can't be empty."); } $names = is_array($path) ? $path : [$path]; $this->_chain = []; $total = count($names); foreach ($names as $index => $name) { if (preg_match('/^::.*/', $name)) { $reference = is_object($reference) ? get_class($reference) : $reference; } $hash = Suite::hash($reference); if (!isset(static::$_registered[$hash])) { static::$_registered[$hash] = new static($reference); } $instance = static::$_registered[$hash]; if (is_object($reference)) { Suite::register(get_class($reference)); } else { Suite::register($reference); } if (!isset($instance->_methods[$name])) { $instance->_methods[$name] = []; $instance->_stubs[$name] = Double::instance(); } $method = new Method(['parent' => $this, 'reference' => $reference, 'name' => $name]); $this->_chain[$name] = $method; array_unshift($instance->_methods[$name], $method); if ($index < $total - 1) { $reference = $instance->_stubs[$name]; $method->andReturn($instance->_stubs[$name]); } } $method = end($this->_chain); if ($closure) { $method->andRun($closure); } return $method; }
$code = "<?php\necho 'Hello World!';\n"; $matcher = function ($actual) use($code) { return $code === (string) $actual; }; expect($stub1)->toReceive('process')->with(Arg::toMatch($matcher), $path); expect($stub2)->toReceive('process')->with(Arg::toMatch($matcher), $path); $this->patchers->process($code, $path); }); it("bails out if code to process is an empty string", function () { expect($this->patchers->process(''))->toBe(''); }); }); describe("->findFile()", function () { beforeEach(function () { $this->loader = Double::instance(); $this->class = Double::classname(); $this->file = 'some/path/file.php'; $this->stub1 = Double::instance(); $this->patchers->add('patcher1', $this->stub1); $this->stub2 = Double::instance(); $this->patchers->add('patcher2', $this->stub2); $file = $this->file; allow($this->stub1)->toReceive('findFile')->andRun(function () use($file) { return $file; }); allow($this->stub2)->toReceive('findFile')->andRun(function () use($file) { return $file; }); }); it("runs findFile() on all patchers", function () { expect($this->stub1)->toReceive('findFile')->with($this->loader, $this->class, $this->file);
namespace Chaos\Spec\Suite\Collection; use InvalidArgumentException; use Chaos\Model; use Chaos\Collection\Collection; use Chaos\Collection\Through; use Kahlan\Plugin\Double; use Chaos\Spec\Fixture\Model\Image; use Chaos\Spec\Fixture\Model\Tag; use Chaos\Spec\Fixture\Model\ImageTag; describe("Through", function () { beforeEach(function () { $this->images_tags = []; $this->imageTagModel = $imageTagModel = Double::classname(['extends' => ImageTag::class]); $imageTagModel::definition()->locked(false); $this->tagModel = $tagModel = Double::classname(['extends' => Tag::class, 'methods' => ['tagMethod']]); $tagModel::definition()->locked(false); allow($tagModel)->toReceive('tagMethod')->andRun(function ($options) { return $options; }); for ($i = 0; $i < 5; $i++) { $image_tag = new $imageTagModel(); $tag = new $tagModel(); $tag->name = (string) $i; $image_tag->tag = $tag; $this->images_tags[] = $image_tag; } $this->image = new Image(['data' => ['id' => 1, 'name' => 'amiga_1200.jpg', 'title' => 'Amiga 1200', 'images_tags' => $this->images_tags]]); $this->through = new Through(['schema' => $tagModel::definition(), 'parent' => $this->image, 'through' => 'images_tags', 'using' => 'tag']); $this->image->tags = $this->through; });
}); describe("->is()", function () { beforeEach(function () { $this->checker = Double::classname(['extends' => Checker::class]); $this->validator = new Validator(['classes' => ['checker' => $this->checker]]); }); it("delegates to the checker", function () { $checker = $this->checker; $handler = $checker::get('alphaNumeric'); expect($checker)->toReceive('::check')->with('frferrf', [$handler], ['hello' => 'world']); $this->validator->is('alphaNumeric', 'frferrf', ['hello' => 'world']); }); }); describe("->__call()", function () { beforeEach(function () { $this->checker = Double::classname(['extends' => Checker::class]); $this->validator = new Validator(['classes' => ['checker' => $this->checker]]); }); it("delegates to the checker", function () { $checker = $this->checker; $handler = $checker::get('alphaNumeric'); expect($checker)->toReceive('::check')->with('frferrf', [$handler], ['hello' => 'world']); $this->validator->isAlphaNumeric('frferrf', ['hello' => 'world']); }); it("bails out with no passed parameters", function () { expect($this->validator->isAlphaNumeric())->toBe(false); }); }); describe("->validates()", function () { beforeEach(function () { $this->validator = new Validator();
allow($classname)->toReceive('::magicCallStatic')->andReturn('Good Evening World!', 'Good Bye World!'); expect($classname::magicCallStatic())->toBe('Good Evening World!'); expect($classname::magicCallStatic())->toBe('Good Bye World!'); }); it("produces unique classname", function () { $double = Double::classname(); $double2 = Double::classname(); expect($double)->not->toBe($double2); }); it("stubs classes with `construct()` if no parent defined", function () { $class = Double::classname(); expect($class)->toReceive('__construct'); $double = new $class(); }); it("expects method called in the past to be uncalled", function () { $class = Double::classname(); $class::message(); expect($class)->not->toReceive('::message'); }); }); describe("::generate()", function () { it("throws an exception with an unexisting trait", function () { expect(function () { Double::generate(['uses' => ['an\\unexisting\\Trait']]); })->toThrow(); }); it("throws an exception with an unexisting interface", function () { expect(function () { Double::generate(['implements' => ['an\\unexisting\\Interface']]); })->toThrow(); });
}); describe("->unbind()", function () { it("unbinds a relation", function () { expect($this->schema->hasRelation('gallery'))->toBe(true); $this->schema->unbind('gallery'); expect($this->schema->hasRelation('gallery'))->toBe(false); }); }); describe("->relations", function () { it("returns all relation names", function () { $relations = $this->schema->relations(); sort($relations); expect($relations)->toBe(['gallery', 'images_tags', 'tags']); }); it("includes embedded relations using `true` as first parameter", function () { $model = Double::classname(['extends' => Model::class]); $schema = new Schema(['model' => $model]); $schema->column('embedded', ['type' => 'object', 'model' => $model]); expect($schema->relations())->toBe([]); expect($schema->relations(true))->toBe(['embedded']); }); }); describe("->conventions()", function () { it("gets/sets the conventions", function () { $conventions = Double::instance(); $schema = new Schema(); expect($schema->conventions($conventions))->toBe($schema); expect($schema->conventions())->toBe($conventions); }); }); describe("->expand()", function () {