├── .gitignore
├── package.json
├── composer.json
├── classes
└── WPCC
│ ├── Component
│ ├── Generator
│ │ ├── Actor.php
│ │ ├── Sequence.php
│ │ └── Sequence
│ │ │ └── Faker.php
│ ├── Factory
│ │ ├── Callback
│ │ │ └── AfterCreate.php
│ │ ├── Attachment.php
│ │ ├── User.php
│ │ ├── Post.php
│ │ ├── Blog.php
│ │ ├── Network.php
│ │ ├── Comment.php
│ │ └── Term.php
│ ├── Console
│ │ └── Input
│ │ │ └── ArgvInput.php
│ └── Factory.php
│ ├── Helper
│ ├── PageObject
│ │ └── LoginPage.php
│ ├── PageObject.php
│ └── Factory.php
│ ├── SuiteManager.php
│ ├── Subscriber
│ ├── Bootstrap.php
│ └── AutoRebuild.php
│ ├── Util
│ └── Debug.php
│ ├── Configuration.php
│ ├── Command
│ ├── Build.php
│ ├── Bootstrap.php
│ └── Run.php
│ ├── Module
│ ├── BrowserStack.php
│ ├── WebDriver.php
│ ├── WordPress.php
│ ├── Wpdb.php
│ └── MailtrapIO.php
│ ├── Codecept.php
│ ├── CLI
│ ├── Selenium.php
│ └── Codeception.php
│ └── TestLoader.php
├── wp-codeception.php
├── README.md
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /vendor/
3 | /logs/
4 | /npm-debug.log
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wp-codeception",
3 | "version": "1.0.2",
4 | "description": "",
5 | "repository": {
6 | "type": "git",
7 | "url": ""
8 | },
9 | "author": "10up Inc",
10 | "license": "GPL-2.0",
11 | "devDependencies": {
12 | "phantomjs": "^1.9.16",
13 | "selenium-server": "^2.45.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "10up/wp-codeception",
3 | "description": "The WordPress Plugin which integrates with the Codeception PHP testing framework and allows you to write and run Codeception tests for WordPress via WP CLI.",
4 | "version": "1.0.3",
5 | "type": "wordpress-plugin",
6 | "keywords": [
7 | "wordpress",
8 | "bdd",
9 | "acceptance testing",
10 | "functional testing",
11 | "unit testing",
12 | "tdd"
13 | ],
14 | "authors": [
15 | {
16 | "name": "10up Inc",
17 | "homepage": "http://10up.com/"
18 | }
19 | ],
20 | "require": {
21 | "php": ">=5.4.0",
22 | "codeception/codeception": "2.0.11",
23 | "fzaninotto/faker": "1.5.*@dev",
24 | "composer/installers": "^1.0"
25 | },
26 | "autoload": {
27 | "psr-0": {
28 | "WPCC": "classes"
29 | }
30 | },
31 | "scripts": {
32 | "post-install-cmd": [
33 | "npm install"
34 | ]
35 | }
36 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Generator/Actor.php:
--------------------------------------------------------------------------------
1 | settings = $settings;
45 | $this->modules = Configuration::modules( $settings );
46 | $this->actions = Configuration::actions( $this->modules );
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/classes/WPCC/Helper/PageObject/LoginPage.php:
--------------------------------------------------------------------------------
1 | _actor;
49 |
50 | $I->amOnPage( wp_login_url() );
51 | $I->fillField( self::USERNAME_FIELD, $username );
52 | $I->fillField( self::PASSWORD_FIELD, $password );
53 | $I->click( self::LOGIN_BUTTON );
54 | $I->seeCookie( LOGGED_IN_COOKIE );
55 |
56 | return $this;
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/classes/WPCC/Helper/PageObject.php:
--------------------------------------------------------------------------------
1 | _actor = $I;
53 | }
54 |
55 | /**
56 | * Creates new instance of page object for an actor and returns it.
57 | *
58 | * @since 1.0.0
59 | *
60 | * @static
61 | * @access public
62 | * @param \Codeception\Actor $I The actor to create a page object for.
63 | * @return \WPCC\Helper\PageObject The new instance of a page object.
64 | */
65 | public static function of( \Codeception\Actor $I ) {
66 | return new static( $I );
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Factory/Callback/AfterCreate.php:
--------------------------------------------------------------------------------
1 | callback = $callback;
53 | }
54 |
55 | /**
56 | * Calls the callback and returns results of the call.
57 | *
58 | * @since 1.0.0
59 | *
60 | * @access public
61 | * @param mixed $object The incoming object.
62 | * @return mixed The results of the callback call.
63 | */
64 | public function call( $object ) {
65 | return call_user_func( $this->callback, $object );
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/classes/WPCC/SuiteManager.php:
--------------------------------------------------------------------------------
1 | settings['path'] );
41 | if ( ! empty( $test ) && has_action( $test ) ) {
42 | $testLoader->loadTest( $test );
43 | } else {
44 | $testLoader->loadTests();
45 | }
46 |
47 | $tests = $testLoader->getTests();
48 | foreach ( $tests as $test ) {
49 | $this->addToSuite( $test );
50 | }
51 | }
52 |
53 | /**
54 | * Initializes modules.
55 | *
56 | * @since 1.0.0
57 | *
58 | * @access protected
59 | */
60 | protected function initializeModules() {
61 | self::$modules = Configuration::modules( $this->settings );
62 | self::$actions = Configuration::actions( self::$modules );
63 |
64 | foreach ( self::$modules as $module ) {
65 | $module->_initialize();
66 | }
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/classes/WPCC/Subscriber/Bootstrap.php:
--------------------------------------------------------------------------------
1 | getSettings();
46 | if ( ! isset( $settings['bootstrap'] ) || ! filter_var( $settings['bootstrap'], FILTER_VALIDATE_BOOLEAN ) ) {
47 | return;
48 | }
49 |
50 | $bootstrap = $settings['path'] . $settings['bootstrap'];
51 | if ( ! is_file( $bootstrap ) ) {
52 | throw new ConfigurationException( "Bootstrap file {$bootstrap} can't be loaded" );
53 | }
54 |
55 | require_once $bootstrap;
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Generator/Sequence.php:
--------------------------------------------------------------------------------
1 | _index = $start;
64 | $this->_template = $template;
65 | }
66 |
67 | /**
68 | * Generates next string and shifts sequence index.
69 | *
70 | * @since 1.0.0
71 | *
72 | * @access public
73 | * @return string A string based on the sequence template.
74 | */
75 | public function next() {
76 | return sprintf( $this->_template, $this->_index++ );
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Generator/Sequence/Faker.php:
--------------------------------------------------------------------------------
1 | _faker = \Faker\Factory::create();
57 | }
58 |
59 | /**
60 | * Generates next string and shifts sequence index.
61 | *
62 | * @since 1.0.0
63 | *
64 | * @access public
65 | * @return string A string based on the sequence template.
66 | */
67 | public function next() {
68 | $this->_index++;
69 | return $this->_faker->{$this->_template};
70 | }
71 |
72 | }
--------------------------------------------------------------------------------
/classes/WPCC/Util/Debug.php:
--------------------------------------------------------------------------------
1 |
37 | *
46 | *
47 | *
48 | * @since 1.0.2
49 | * @uses sprintf() to build debug message.
50 | * @uses \Codeception\Util\Debug::debug() to print data to screen.
51 | *
52 | * @static
53 | * @access public
54 | * @param string $message The message pattern.
55 | * @param type $args
56 | */
57 | public static function debugf( $message, $args ) {
58 | $args = array_slice( func_get_args(), 1 );
59 | if ( count( $args ) == 1 && is_array( $args[0] ) ) {
60 | $args = $args[0];
61 | }
62 |
63 | array_unshift( $args, $message );
64 | $message = call_user_func_array( 'sprintf', $args );
65 | self::debug( $message );
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/classes/WPCC/Subscriber/AutoRebuild.php:
--------------------------------------------------------------------------------
1 | getSettings();
46 | $guyFile = $settings['path'] . $settings['class_name'] . '.php';
47 |
48 | // load guy class to see hash
49 | $handle = fopen( $guyFile, "r" );
50 | if ( $handle ) {
51 | $line = fgets( $handle );
52 | if ( preg_match( '~\[STAMP\] ([a-f0-9]*)~', $line, $matches ) ) {
53 | $hash = $matches[1];
54 | $currentHash = Actor::genHash( SuiteManager::$actions, $settings );
55 |
56 | // regenerate guy class when hashes do not match
57 | if ( $hash != $currentHash ) {
58 | codecept_debug( "Rebuilding {$settings['class_name']}..." );
59 | $guyGenerator = new Actor( $settings );
60 | fclose( $handle );
61 | $generated = $guyGenerator->produce();
62 | file_put_contents( $guyFile, $generated );
63 | return;
64 | }
65 | }
66 |
67 | fclose( $handle );
68 | }
69 | }
70 |
71 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Console/Input/ArgvInput.php:
--------------------------------------------------------------------------------
1 | getGlobalConfig( $configFile );
46 | $suites = $this->getSuites( $configFile );
47 |
48 | $path = pathinfo( $configFile );
49 | $dir = isset( $path['dirname'] ) ? $path['dirname'] : getcwd();
50 |
51 | foreach ( $config['include'] as $subConfig ) {
52 | $this->output->writeln( "Included Configuration: $subConfig" );
53 | $this->buildActorsForConfig( $dir . DIRECTORY_SEPARATOR . $subConfig );
54 | }
55 |
56 | if ( !empty( $suites ) ) {
57 | $this->output->writeln( "Building Actor classes for suites: " . implode( ', ', $suites ) . '' );
58 | }
59 | foreach ( $suites as $suite ) {
60 | $settings = $this->getSuiteConfig( $suite, $configFile );
61 | $gen = new ActorGenerator( $settings );
62 | $this->output->writeln( '' . Configuration::config()['namespace'] . '\\' . $gen->getActorName() . " includes modules: " . implode( ', ', $gen->getModules() ) );
63 | $contents = $gen->produce();
64 |
65 | @mkdir( $settings['path'], 0755, true );
66 | $file = $settings['path'] . $this->getClassName( $settings['class_name'] ) . '.php';
67 | $this->save( $file, $contents, true );
68 | $this->output->writeln( "{$settings['class_name']}.php generated successfully. " . $gen->getNumMethods() . " methods added" );
69 | }
70 | }
71 |
72 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Factory/Attachment.php:
--------------------------------------------------------------------------------
1 | _debug( 'Reading mime type of the file: ' . $args['file'] );
46 |
47 | $filetype = wp_check_filetype( basename( $args['file'] ), null );
48 | if ( ! empty( $filetype['type'] ) ) {
49 | $args['post_mime_type'] = $filetype['type'];
50 | $this->_debug( 'Mime type found: ' . $filetype['type'] );
51 | } else {
52 | $this->_debug( 'Mime type not found' );
53 | }
54 | }
55 | }
56 |
57 | $attachment_id = wp_insert_attachment( $args );
58 | if ( $attachment_id ) {
59 | $this->_debug(
60 | 'Generated attachment ID: %d (file: %s)',
61 | $attachment_id,
62 | ! empty( $args['file'] ) ? $args['file'] : 'not-provided'
63 | );
64 |
65 | $this->_debug( 'Generating attachment metadata' );
66 | $metadata = wp_generate_attachment_metadata( $attachment_id, $args['file'] );
67 | if ( is_wp_error( $metadata ) ) {
68 | $this->_debug(
69 | 'Attachment metadata generation failed with error [%s] %s',
70 | $metadata->get_error_code(),
71 | $metadata->get_error_message()
72 | );
73 | } elseif ( empty( $metadata ) ) {
74 | $this->_debug( 'Attachment metadata generation failed' );
75 | } else {
76 | wp_update_attachment_metadata( $attachment_id, $metadata );
77 | }
78 | } else {
79 | $this->_debug( 'Attachment generation failed' );
80 | }
81 |
82 | return $attachment_id;
83 | }
84 |
85 | }
--------------------------------------------------------------------------------
/classes/WPCC/Module/BrowserStack.php:
--------------------------------------------------------------------------------
1 | requiredFields[] = 'username';
45 | $this->requiredFields[] = 'access_key';
46 |
47 | parent::__construct( $config );
48 | }
49 |
50 | /**
51 | * Initializes webdriver.
52 | *
53 | * @since 1.0.0
54 | *
55 | * @access public
56 | */
57 | public function _initialize() {
58 | $this->wd_host = sprintf( 'http://%s:%s@hub.browserstack.com/wd/hub', $this->config['username'], $this->config['access_key'] );
59 |
60 | $this->capabilities = $this->config['capabilities'];
61 | $this->capabilities[ \WebDriverCapabilityType::BROWSER_NAME ] = $this->config['browser'];
62 | if ( ! empty( $this->config['version'] ) ) {
63 | $this->capabilities[ \WebDriverCapabilityType::VERSION ] = $this->config['version'];
64 | }
65 |
66 | $this->webDriver = \RemoteWebDriver::create( $this->wd_host, $this->capabilities );
67 | $this->webDriver->manage()->timeouts()->implicitlyWait( $this->config['wait'] );
68 |
69 | $this->initialWindowSize();
70 | }
71 |
72 | /**
73 | * Setup initial window size.
74 | *
75 | * @since 1.0.0
76 | *
77 | * @access protected
78 | */
79 | protected function initialWindowSize() {
80 | if ( isset( $this->config['resolution'] ) ) {
81 | if ( $this->config['resolution'] == 'maximize' ) {
82 | $this->maximizeWindow();
83 | } else {
84 | $size = explode( 'x', $this->config['resolution'] );
85 | if ( count( $size ) == 2 ) {
86 | $this->resizeWindow( intval( $size[0] ), intval( $size[1] ) );
87 | }
88 | }
89 | }
90 | }
91 |
92 | }
--------------------------------------------------------------------------------
/classes/WPCC/Codecept.php:
--------------------------------------------------------------------------------
1 | dispatcher->addSubscriber( new \Codeception\Subscriber\ErrorHandler() );
41 | $this->dispatcher->addSubscriber( new \WPCC\Subscriber\Bootstrap() );
42 | $this->dispatcher->addSubscriber( new \Codeception\Subscriber\Module() );
43 | $this->dispatcher->addSubscriber( new \Codeception\Subscriber\BeforeAfterTest() );
44 | $this->dispatcher->addSubscriber( new \WPCC\Subscriber\AutoRebuild() );
45 |
46 | // optional
47 | if ( !$this->options['silent'] ) {
48 | $this->dispatcher->addSubscriber( new \Codeception\Subscriber\Console( $this->options ) );
49 | }
50 |
51 | if ( $this->options['fail-fast'] ) {
52 | $this->dispatcher->addSubscriber( new \Codeception\Subscriber\FailFast() );
53 | }
54 |
55 | if ( $this->options['coverage'] ) {
56 | $this->dispatcher->addSubscriber( new \Codeception\Coverage\Subscriber\Local( $this->options ) );
57 | $this->dispatcher->addSubscriber( new \Codeception\Coverage\Subscriber\LocalServer( $this->options ) );
58 | $this->dispatcher->addSubscriber( new \Codeception\Coverage\Subscriber\RemoteServer( $this->options ) );
59 | $this->dispatcher->addSubscriber( new \Codeception\Coverage\Subscriber\Printer( $this->options ) );
60 | }
61 |
62 | // extensions
63 | foreach ( $this->extensions as $subscriber ) {
64 | $this->dispatcher->addSubscriber( $subscriber );
65 | }
66 | }
67 |
68 | /**
69 | * Runs suite tests.
70 | *
71 | * @since 1.0.0
72 | *
73 | * @access public
74 | * @param array $settings The array of suite settings.
75 | * @param string $suite The suite name to run.
76 | * @param string $test The test name to run.
77 | * @return \PHPUnit_Framework_TestResult The suite execution results.
78 | */
79 | public function runSuite( $settings, $suite, $test = null ) {
80 | $suiteManager = new SuiteManager( $this->dispatcher, $suite, $settings );
81 | $suiteManager->initialize();
82 | $suiteManager->loadTests( $test );
83 | $suiteManager->run( $this->runner, $this->result, $this->options );
84 |
85 | return $this->result;
86 | }
87 |
88 | }
--------------------------------------------------------------------------------
/classes/WPCC/Module/WebDriver.php:
--------------------------------------------------------------------------------
1 | requiredFields );
43 | if ( ! empty( $url_index ) ) {
44 | unset( $this->requiredFields[ $url_index ] );
45 | }
46 |
47 | // add home url to the config
48 | $this->config['url'] = home_url();
49 |
50 | // call parent constructor
51 | parent::__construct( $config );
52 |
53 | // add pahntomjs path if needed
54 | if ( 'phantomjs' == $this->config['browser'] ) {
55 | $phantomjs_binary = WPCC_ABSPATH . '/node_modules/phantomjs/bin/phantomjs';
56 | if ( ! isset( $this->config['capabilities'] ) ) {
57 | $this->config['capabilities'] = array(
58 | 'phantomjs.binary.path' => $phantomjs_binary,
59 | );
60 | } elseif ( empty( $this->config['capabilities']['phantomjs.binary.path'] ) ) {
61 | $this->config['capabilities']['phantomjs.binary.path'] = $phantomjs_binary;
62 | }
63 | }
64 | }
65 |
66 | /**
67 | * Goes to a specific admin page. Uses amOnPage method to do a redirect.
68 | *
69 | * @since 1.0.0
70 | *
71 | * @access public
72 | * @param string $path Optional path relative to the admin url.
73 | */
74 | public function amOnAdminPage( $path = '' ) {
75 | $this->amOnPage( admin_url( $path ) );
76 | }
77 |
78 | /**
79 | * Clicks admin menu item. Since we are using a WebDriver the first click on
80 | * a parent element will lead to opening a submenu group. So if we need to
81 | * click on submenu item, we need to do it in two steps.
82 | *
83 | * @since 1.0.0
84 | *
85 | * @access public
86 | * @param string $menu The menu item to click on.
87 | * @param string $parent The parent menu item to click on first.
88 | */
89 | public function clickAdminMenu( $menu, $parent = null ) {
90 | if ( $parent ) {
91 | $this->click( $parent, '#adminmenu' );
92 | }
93 |
94 | $this->click( $menu, '#adminmenu' );
95 | }
96 |
97 | /**
98 | * Fills TinyMCE editor.
99 | *
100 | *
101 | * fillTinyMCEField( 'content', 'Lorem ipsum dolor sit...' );
103 | * ?>
104 | *
105 | *
106 | * @since 1.0.0
107 | *
108 | * @access public
109 | * @param string $editor_id The TinyMCE's editor id.
110 | * @param string $value The value to insert into the editor.
111 | */
112 | public function fillTinyMCEField( $editor_id, $value ) {
113 | $script = sprintf( "tinyMCE.get('%s').setContent('%s')", esc_js( $editor_id ), esc_js( $value ) );
114 | $this->webDriver->executeScript( $script );
115 | }
116 |
117 | }
--------------------------------------------------------------------------------
/classes/WPCC/CLI/Selenium.php:
--------------------------------------------------------------------------------
1 | _get_executable();
66 | $pids = trim( shell_exec( "pgrep -l -f {$selenium}" ) );
67 | $pids = explode( PHP_EOL, (string) $pids );
68 |
69 | if ( ! empty( $pids ) && count( $pids ) >= 1 ) {
70 | foreach ( $pids as $pid ) {
71 | shell_exec( "kill -15 {$pid} > /dev/null 2>/dev/null" );
72 | }
73 | \WP_CLI::success( 'Selenium server is stopped.' );
74 | } else {
75 | \WP_CLI::warning( 'Selenium server is not started yet.' );
76 | }
77 | }
78 |
79 | /**
80 | * Starts selenium server.
81 | *
82 | * ### OPTIONS
83 | *
84 | * ### EXAMPLE
85 | *
86 | * wp selenium start
87 | *
88 | * @since 1.0.0
89 | * @alias run
90 | *
91 | * @access public
92 | * @param array $args Unassociated array of arguments passed to this command.
93 | * @param array $assoc_args Associated array of arguments passed to this command.
94 | */
95 | public function start( $args, $assoc_args ) {
96 | $selenium = $this->_get_executable();
97 | if ( is_executable( $selenium ) ) {
98 |
99 | $pids = explode( PHP_EOL, trim( shell_exec( "pgrep -f {$selenium}" ) ) );
100 | if ( count( $pids ) < 2 ) {
101 | shell_exec( "{$selenium} > /dev/null 2>/dev/null &" );
102 | \WP_CLI::success( 'Selenium server started.' );
103 | } else {
104 | \WP_CLI::warning( 'Selenium server is already started.' );
105 | }
106 |
107 | } else {
108 | \WP_CLI::error( 'Selenium server is not executable or not installed.' );
109 | }
110 | }
111 |
112 | /**
113 | * Restarts selenium server.
114 | *
115 | * ### OPTIONS
116 | *
117 | * ### EXAMPLE
118 | *
119 | * wp selenium restart
120 | *
121 | * @since 1.0.0
122 | *
123 | * @access public
124 | * @param array $args Unassociated array of arguments passed to this command.
125 | * @param array $assoc_args Associated array of arguments passed to this command.
126 | */
127 | public function restart( $args, $assoc_args ) {
128 | $this->stop( $args, $assoc_args );
129 | $this->start( $args, $assoc_args );
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/classes/WPCC/Command/Bootstrap.php:
--------------------------------------------------------------------------------
1 | $actor . $this->actorSuffix,
46 | 'modules' => array(
47 | 'enabled' => array(
48 | 'WebDriver',
49 | 'WordPress',
50 | "{$actor}Helper",
51 | ),
52 | 'config' => array(
53 | 'WebDriver' => array(
54 | 'browser' => 'phantomjs',
55 | ),
56 | ),
57 | ),
58 | );
59 |
60 | $str = "# Codeception Test Suite Configuration\n\n";
61 | $str .= "# suite for acceptance tests.\n";
62 | $str .= "# perform tests in browser using the WebDriver or PhpBrowser.\n";
63 | $str .= "# If you need both WebDriver and PHPBrowser tests - create a separate suite.\n\n";
64 |
65 | $str .= Yaml::dump( $suiteConfig, 5 );
66 | $this->createSuite( 'acceptance', $actor, $str );
67 | }
68 |
69 | /**
70 | * Creates functional suite.
71 | *
72 | * @since 1.0.0
73 | *
74 | * @access protected
75 | * @param string $actor The actor name.
76 | */
77 | protected function createFunctionalSuite( $actor = 'Functional' ) {
78 | $suiteConfig = array(
79 | 'class_name' => $actor . $this->actorSuffix,
80 | 'modules' => array(
81 | 'enabled' => array( 'WordPress', $actor . 'Helper' )
82 | ),
83 | );
84 |
85 | $str = "# Codeception Test Suite Configuration\n\n";
86 | $str .= "# suite for functional (integration) tests.\n";
87 | $str .= "# emulate web requests and make application process them.\n\n";
88 | $str .= Yaml::dump( $suiteConfig, 2 );
89 |
90 | $this->createSuite( 'functional', $actor, $str );
91 | }
92 |
93 | /**
94 | * Creates global config file.
95 | *
96 | * @since 1.0.0
97 | *
98 | * @access public
99 | */
100 | public function createGlobalConfig() {
101 | $basicConfig = array(
102 | 'actor' => $this->actorSuffix,
103 | 'paths' => array(
104 | 'tests' => 'tests',
105 | 'log' => $this->logDir,
106 | 'data' => $this->dataDir,
107 | 'helpers' => $this->helperDir
108 | ),
109 | 'settings' => array(
110 | 'bootstrap' => '_bootstrap.php',
111 | 'colors' => strtoupper( substr( PHP_OS, 0, 3 ) ) != 'WIN',
112 | 'memory_limit' => WP_MAX_MEMORY_LIMIT
113 | ),
114 | );
115 |
116 | $str = Yaml::dump( $basicConfig, 4 );
117 | if ( $this->namespace ) {
118 | $str = "namespace: {$this->namespace} \n" . $str;
119 | }
120 |
121 | file_put_contents( 'codeception.yml', $str );
122 | }
123 |
124 | /**
125 | * Creates appropriate folders.
126 | *
127 | * @since 1.0.0
128 | *
129 | * @access protected
130 | */
131 | protected function createDirs() {
132 | @mkdir( 'tests' );
133 | @mkdir( $this->logDir );
134 | @mkdir( $this->dataDir );
135 | @mkdir( $this->helperDir );
136 | }
137 |
138 | }
--------------------------------------------------------------------------------
/classes/WPCC/Module/WordPress.php:
--------------------------------------------------------------------------------
1 | assertNotEmpty( $metas, $message );
48 |
49 | if ( func_num_args() > 2 ) {
50 | $message = sprintf( 'User meta %s does not contain expected value', $meta_key );
51 | $this->assertContains( $meta_value, $metas, $message );
52 | }
53 | }
54 |
55 | /**
56 | * Checks if user meta doesn't exists.
57 | *
58 | * @since 1.0.0
59 | *
60 | * @access public
61 | * @param int $user_id The user id.
62 | * @param string $meta_key The meta key to check.
63 | * @param mixed $meta_value The meta value to check
64 | */
65 | public function dontSeeUserMetaFor( $user_id, $meta_key, $meta_value = null ) {
66 | $metas = get_user_meta( $user_id, $meta_key );
67 |
68 | if ( func_num_args() > 2 ) {
69 | $message = sprintf( 'User meta %s contains not expected value', $meta_key );
70 | $this->assertNotContains( $meta_value, $metas, $message );
71 | } else {
72 | $message = sprintf( 'User meta %s is not empty', $meta_key );
73 | $this->assertEmpty( $metas, $message );
74 | }
75 | }
76 |
77 | /**
78 | * Checks a post meta exists for a post.
79 | *
80 | * @since 1.0.0
81 | *
82 | * @access public
83 | * @param int $post_id The post id.
84 | * @param string $meta_key The meta key to check.
85 | * @param mixed $meta_value The meta value to check
86 | */
87 | public function seePostMetaFor( $post_id, $meta_key, $meta_value = null ) {
88 | $metas = get_post_meta( $post_id, $meta_key );
89 |
90 | $message = sprintf( 'Post meta %s does not exist', $meta_key );
91 | $this->assertNotEmpty( $metas, $message );
92 |
93 | if ( func_num_args() > 2 ) {
94 | $message = sprintf( 'Post meta %s does not contain expected value', $meta_key );
95 | $this->assertContains( $meta_value, $metas, $message );
96 | }
97 | }
98 |
99 | /**
100 | * Checks if post meta doesn't exists.
101 | *
102 | * @since 1.0.0
103 | *
104 | * @access public
105 | * @param int $post The post id.
106 | * @param string $meta_key The meta key to check.
107 | * @param mixed $meta_value The meta value to check
108 | */
109 | public function dontSeePostMetaFor( $post, $meta_key, $meta_value = null ) {
110 | $metas = get_post_meta( $post, $meta_key );
111 |
112 | if ( func_num_args() > 2 ) {
113 | $message = sprintf( 'Post meta %s contains not expected value', $meta_key );
114 | $this->assertNotContains( $meta_value, $metas, $message );
115 | } else {
116 | $message = sprintf( 'Post meta %s is not empty', $meta_key );
117 | $this->assertEmpty( $metas, $message );
118 | }
119 | }
120 |
121 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Factory/User.php:
--------------------------------------------------------------------------------
1 | new FakerSequence( 'name' ),
45 | 'user_pass' => 'password',
46 | 'user_email' => new FakerSequence( 'email' ),
47 | ) );
48 | }
49 |
50 | /**
51 | * Generates a new user.
52 | *
53 | * @since 1.0.0
54 | *
55 | * @access protected
56 | * @param array $args The array of arguments to use during a new user creation.
57 | * @return int|\WP_Error The newly created user's ID on success, otherwise a WP_Error object.
58 | */
59 | protected function _createObject( $args ) {
60 | $user_id = wp_insert_user( $args );
61 | if ( $user_id && ! is_wp_error( $user_id ) ) {
62 | $this->_debug( 'Generated user with ID: ' . $user_id );
63 | } elseif ( is_wp_error( $user_id ) ) {
64 | $this->_debug(
65 | 'User generation failed with message [%s] %s',
66 | $user_id->get_error_code(),
67 | $user_id->get_error_message()
68 | );
69 | } else {
70 | $this->_debug( 'User generation failed' );
71 | }
72 |
73 |
74 | return $user_id;
75 | }
76 |
77 | /**
78 | * Updates generated user.
79 | *
80 | * @since 1.0.0
81 | *
82 | * @access protected
83 | * @param mixed $user_id The user id to update.
84 | * @param array $fields The array of fields to update.
85 | * @return mixed Updated user ID on success, otherwise a WP_Error object.
86 | */
87 | protected function _updateObject( $user_id, $fields ) {
88 | $fields['ID'] = $user_id;
89 | $updated = wp_update_user( $fields );
90 | if ( $updated && ! is_wp_error( $updated ) ) {
91 | $this->_debug( 'Updated user ' . $user_id );
92 | } elseif ( is_wp_error( $updated ) ) {
93 | $this->_debug(
94 | 'Update failed for user %d with message [%s] %s',
95 | $user_id,
96 | $updated->get_error_code(),
97 | $updated->get_error_message()
98 | );
99 | } else {
100 | $this->_debug( 'Update failed for user ' . $user_id );
101 | }
102 |
103 | return $updated;
104 | }
105 |
106 | /**
107 | * Deletes previously generated user.
108 | *
109 | * @since 1.0.0
110 | *
111 | * @access protected
112 | * @param int $user_id The user id to delete.
113 | * @return boolean TRUEY on success, otherwise FALSE.
114 | */
115 | protected function _deleteObject( $user_id ) {
116 | $user = get_user_by( 'id', $user_id );
117 | if ( ! $user ) {
118 | return false;
119 | }
120 |
121 | $deleted = is_multisite()
122 | ? wpmu_delete_user( $user_id )
123 | : wp_delete_user( $user_id );
124 |
125 | if ( $deleted ) {
126 | $this->_debug( 'Deleted user with ID: ' . $user_id );
127 | return true;
128 | }
129 |
130 | $this->_debug( 'User removal failed for ID: ' . $user_id );
131 | return false;
132 | }
133 |
134 | /**
135 | * Returns generated user by id.
136 | *
137 | * @since 1.0.0
138 | *
139 | * @access public
140 | * @param int $user_id The user id.
141 | * @return \WP_User The generated user.
142 | */
143 | public function getObjectById( $user_id ) {
144 | return new \WP_User( $user_id );
145 | }
146 |
147 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Factory/Post.php:
--------------------------------------------------------------------------------
1 | 'publish',
45 | 'post_title' => new Sequence( 'Post title %s' ),
46 | 'post_content' => new Sequence( 'Post content %s' ),
47 | 'post_excerpt' => new Sequence( 'Post excerpt %s' ),
48 | 'post_type' => 'post'
49 | ) );
50 | }
51 |
52 | /**
53 | * Generates a new post.
54 | *
55 | * @since 1.0.0
56 | *
57 | * @access protected
58 | * @param array $args The array of arguments to use during a new post creation.
59 | * @return int|\WP_Error The newly created post's ID on success, otherwise a WP_Error object.
60 | */
61 | protected function _createObject( $args ) {
62 | $post_id = wp_insert_post( $args, true );
63 | if ( $post_id && ! is_wp_error( $post_id ) ) {
64 | $this->_debug( 'Generated post ID: ' . $post_id );
65 | } elseif ( is_wp_error( $post_id ) ) {
66 | $this->_debug(
67 | 'Post generation failed with message [%s] %s',
68 | $post_id->get_error_code(),
69 | $post_id->get_error_messages()
70 | );
71 | } else {
72 | $this->_debug( 'Post generation failed' );
73 | }
74 |
75 | return $post_id;
76 | }
77 |
78 | /**
79 | * Updates generated object.
80 | *
81 | * @since 1.0.0
82 | *
83 | * @access protected
84 | * @param mixed $post_id The post id to update.
85 | * @param array $fields The array of fields to update.
86 | * @return mixed Updated post ID on success, otherwise 0 or a WP_Error object.
87 | */
88 | protected function _updateObject( $post_id, $fields ) {
89 | $fields['ID'] = $post_id;
90 | $updated = wp_update_post( $fields );
91 | if ( $updated && ! is_wp_error( $updated ) ) {
92 | $this->_debug( 'Updated post ' . $post_id );
93 | } elseif ( is_wp_error( $updated ) ) {
94 | $this->_debug(
95 | 'Update failed for post %d with message [%s] %s',
96 | $post_id,
97 | $updated->get_error_code(),
98 | $updated->get_error_message()
99 | );
100 | } else {
101 | $this->_debug( 'Update failed for post ' . $post_id );
102 | }
103 |
104 | return $updated;
105 | }
106 |
107 | /**
108 | * Deletes previously generated post object.
109 | *
110 | * @since 1.0.0
111 | *
112 | * @access protected
113 | * @param int $post_id The post id to delete.
114 | * @return boolean TRUE on success, otherwise FALSE.
115 | */
116 | protected function _deleteObject( $post_id ) {
117 | $post = get_post( $post_id );
118 | if ( ! $post ) {
119 | return false;
120 | }
121 |
122 | $deleted = wp_delete_post( $post_id, true );
123 | if ( $deleted ) {
124 | $this->_debug( 'Deleted post with ID: ' . $post_id );
125 | return true;
126 | }
127 |
128 | $this->_debug( 'Post removal failed for ID: ' . $post_id );
129 | return false;
130 | }
131 |
132 | /**
133 | * Returns generated post by id.
134 | *
135 | * @since 1.0.0
136 | *
137 | * @access public
138 | * @param int $post_id The post id.
139 | * @return \WP_Post|null The generated post on success, otherwise NULL.
140 | */
141 | public function getObjectById( $post_id ) {
142 | return get_post( $post_id );
143 | }
144 |
145 | }
--------------------------------------------------------------------------------
/classes/WPCC/Helper/Factory.php:
--------------------------------------------------------------------------------
1 | _factories = array(
63 | 'user' => new \WPCC\Component\Factory\User(),
64 | 'post' => new \WPCC\Component\Factory\Post(),
65 | 'attachment' => new \WPCC\Component\Factory\Attachment(),
66 | 'comment' => new \WPCC\Component\Factory\Comment(),
67 | 'term' => new TermsFactory(),
68 | 'category' => new TermsFactory( 'category' ),
69 | 'tag' => new TermsFactory( 'post_tag' ),
70 | );
71 |
72 | if ( is_multisite() ) {
73 | $this->_factories['blog'] = new \WPCC\Component\Factory\Blog();
74 | $this->_factories['network'] = new \WPCC\Component\Factory\Network();
75 | }
76 | }
77 |
78 | /**
79 | * Creates a new instance of the factory and returns it.
80 | *
81 | * @since 1.0.0
82 | *
83 | * @static
84 | * @access public
85 | * @return \WPCC\Helper\Factory The factory instance.
86 | */
87 | public static function create() {
88 | return new static();
89 | }
90 |
91 | /**
92 | * Returns concrete factory.
93 | *
94 | * @since 1.0.0
95 | *
96 | * @access public
97 | * @param string $factory The factory name.
98 | * @return \WPCC\Component\Factory The factory object if available, otherwise NULL.
99 | */
100 | public function __get( $factory ) {
101 | return isset( $this->_factories[ $factory ] )
102 | ? $this->_factories[ $factory ]
103 | : null;
104 | }
105 |
106 | /**
107 | * Registers new terms factory.
108 | *
109 | * @since 1.0.0
110 | *
111 | * @access public
112 | * @param string $factory_name The factory name.
113 | * @param string $taxonomy The taxonomy name.
114 | * @return boolean TRUE on success, otherwise FALSE.
115 | */
116 | public function addTermsFactory( $factory_name, $taxonomy ) {
117 | // do nothing if factory name is already taken
118 | if ( ! empty( $this->_factories[ $factory_name ] ) ) {
119 | return false;
120 | }
121 |
122 | // do nothing if a taxonomy doesn't exist
123 | if ( ! taxonomy_exists( $taxonomy ) ) {
124 | return false;
125 | }
126 |
127 | $this->_factories[ $factory_name ] = new TermsFactory( $taxonomy );
128 |
129 | return true;
130 | }
131 |
132 | /**
133 | * Cleans up all factories.
134 | *
135 | * @since 1.0.0
136 | *
137 | * @access public
138 | */
139 | public function cleanup() {
140 | foreach ( $this->_factories as $factory ) {
141 | if ( $factory instanceof \WPCC\Component\Factory ) {
142 | $factory->deleteAll();
143 | }
144 | }
145 | }
146 |
147 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Factory/Blog.php:
--------------------------------------------------------------------------------
1 | $current_site->domain,
50 | 'path' => new Sequence( $base . 'testpath%s' ),
51 | 'title' => new FakerSequence( 'company' ),
52 | 'site_id' => $current_site->id,
53 | ) );
54 | }
55 |
56 | /**
57 | * Generates a new blog.
58 | *
59 | * @since 1.0.0
60 | *
61 | * @access protected
62 | * @global \wpdb $wpdb The database connection.
63 | * @param array $args The array of arguments to use during a new blog creation.
64 | * @return int|\WP_Error The newly created blog's ID on success, otherwise a WP_Error object.
65 | */
66 | protected function _createObject( $args ) {
67 | global $wpdb;
68 |
69 | $meta = isset( $args['meta'] ) ? $args['meta'] : array();
70 | $user_id = isset( $args['user_id'] ) ? $args['user_id'] : get_current_user_id();
71 |
72 | // temp tables will trigger db errors when we attempt to reference them as new temp tables
73 | $suppress = $wpdb->suppress_errors();
74 | $blog = wpmu_create_blog( $args['domain'], $args['path'], $args['title'], $user_id, $meta, $args['site_id'] );
75 | $wpdb->suppress_errors( $suppress );
76 |
77 | if ( $blog && ! is_wp_error( $blog ) ) {
78 | $this->_debug( 'Generated blog ID: ' . $blog );
79 | } elseif ( is_wp_error( $blog ) ) {
80 | $this->_debug(
81 | 'Blog generation failed with message [%s] %s',
82 | $blog->get_error_code(),
83 | $blog->get_error_messages()
84 | );
85 | } else {
86 | $this->_debug( 'Blog generation failed' );
87 | }
88 |
89 | return $blog;
90 | }
91 |
92 | /**
93 | * Does nothing, just implements abstract method.
94 | *
95 | * @since 1.0.0
96 | *
97 | * @access protected
98 | * @param mixed $blog_id The blog id.
99 | * @param array $fields The array of fields to update.
100 | * @return int The blog id.
101 | */
102 | protected function _updateObject( $blog_id, $fields ) {
103 | return $blog_id;
104 | }
105 |
106 | /**
107 | * Deletes previously generated blog.
108 | *
109 | * @since 1.0.0
110 | *
111 | * @access protected
112 | * @global \wpdb $wpdb The database connection.
113 | * @param int $blog_id The blog id.
114 | * @return boolean Always returns TRUE.
115 | */
116 | protected function _deleteObject( $blog_id ) {
117 | global $wpdb;
118 |
119 | $suppress = $wpdb->suppress_errors();
120 | wpmu_delete_blog( $blog_id, true );
121 | $wpdb->suppress_errors( $suppress );
122 |
123 | $this->_debug( 'Deleted blog with ID: ' . $blog_id );
124 |
125 | return true;
126 | }
127 |
128 | /**
129 | * Returns generated blog by id.
130 | *
131 | * @since 1.0.0
132 | *
133 | * @access public
134 | * @param int $blog_id The blog id.
135 | * @return object|boolean The generated blog details on success, otherwise FALSE.
136 | */
137 | public function getObjectById( $blog_id ) {
138 | return get_blog_details( $blog_id, false );
139 | }
140 |
141 | }
--------------------------------------------------------------------------------
/classes/WPCC/TestLoader.php:
--------------------------------------------------------------------------------
1 | setBackupGlobals( false );
49 | $testCase->setBackupStaticAttributes( false );
50 | $testCase->setRunTestInSeparateProcess( false );
51 | $testCase->setInIsolation( false );
52 | $testCase->setPreserveGlobalState( false );
53 |
54 | if ( $testCase instanceof Configurable ) {
55 | $testCase->configName( $name );
56 | $testCase->configFile( $file );
57 | $testCase->initConfig();
58 | }
59 | }
60 |
61 | /**
62 | * Adds Cept test to the tests list.
63 | *
64 | * @sicne 1.0.0
65 | *
66 | * @access public
67 | * @param string $file The Cept test action name.
68 | */
69 | public function addCept( $file ) {
70 | $name = $this->relativeName( $file );
71 |
72 | $cept = new Cept();
73 | $this->_setupTestCase( $cept, $name, $file );
74 |
75 | $this->tests[] = $cept;
76 | }
77 |
78 | /**
79 | * Creates test from Cest method.
80 | *
81 | * @since 1.0.0
82 | *
83 | * @access protected
84 | * @param object $cestInstance The instance of a Cest class.
85 | * @param string $methodName The method name to create test from.
86 | * @param mixed $file Deprecated parameter.
87 | * @return \WPCC\TestCase\Cest Instance of Cest test.
88 | */
89 | protected function createTestFromCestMethod( $cestInstance, $methodName, $file ) {
90 | if ( strpos( $methodName, '_' ) === 0 || $methodName == '__construct' ) {
91 | return null;
92 | }
93 |
94 | $cest = new Cest();
95 | $cest->config( 'testClassInstance', $cestInstance );
96 | $cest->config( 'testMethod', $methodName );
97 |
98 | $this->_setupTestCase( $cest, $methodName, $file );
99 |
100 | $testClass = get_class( $cestInstance );
101 | $cest->getScenario()->env( Annotation::forMethod( $testClass, $methodName )->fetchAll( 'env' ) );
102 | $cest->setDependencies( \PHPUnit_Util_Test::getDependencies( $testClass, $methodName ) );
103 |
104 | return $cest;
105 | }
106 |
107 | /**
108 | * Creates test from PHPUnit method.
109 | *
110 | * @since 1.0.0
111 | *
112 | * @access protected
113 | * @param \ReflectionClass $class The class object.
114 | * @param \ReflectionMethod $method The method object.
115 | * @return \PHPUnit_Framework_Test The test object.
116 | */
117 | protected function createTestFromPhpUnitMethod( \ReflectionClass $class, \ReflectionMethod $method ) {
118 | if ( ! \PHPUnit_Framework_TestSuite::isTestMethod( $method ) ) {
119 | return;
120 | }
121 |
122 | $test = \PHPUnit_Framework_TestSuite::createTest( $class, $method->name );
123 | if ( $test instanceof \PHPUnit_Framework_TestSuite_DataProvider ) {
124 | foreach ( $test->tests() as $t ) {
125 | $this->enhancePhpunitTest( $t );
126 | }
127 | } else {
128 | $this->enhancePhpunitTest( $test );
129 | }
130 |
131 | $test->setBackupGlobals( false );
132 | $test->setBackupStaticAttributes( false );
133 | $test->setRunTestInSeparateProcess( false );
134 | $test->setInIsolation( false );
135 | $test->setPreserveGlobalState( false );
136 |
137 | return $test;
138 | }
139 |
140 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Factory/Network.php:
--------------------------------------------------------------------------------
1 | domainName;
45 |
46 | parent::__construct( array(
47 | 'domain' => $domain,
48 | 'title' => new Sequence( 'Network %s' ),
49 | 'path' => new Sequence( '/testpath%s/' ),
50 | 'network_id' => new Sequence( '%s', 2 ),
51 | 'subdomain_install' => false,
52 | ) );
53 | }
54 |
55 | /**
56 | * Generates a new network.
57 | *
58 | * @since 1.0.0
59 | *
60 | * @access protected
61 | * @param array $args The array of arguments to use during a new object creation.
62 | * @return int The newly created network's ID.
63 | */
64 | protected function _createObject( $args ) {
65 | require_once ABSPATH . 'wp-admin/includes/upgrade.php';
66 |
67 | $faker = \Faker\Factory::create();
68 | $test_email = defined( 'WP_TESTS_EMAIL' ) ? WP_TESTS_EMAIL : $faker->email;
69 |
70 | $email = isset( $args['user'] )
71 | ? get_userdata( $args['user'] )->user_email
72 | : $test_email;
73 |
74 | $network = populate_network( $args['network_id'], $args['domain'], $email, $args['title'], $args['path'], $args['subdomain_install'] );
75 |
76 | if ( $network && ! is_wp_error( $network ) ) {
77 | $this->_debug( 'Generated network ID: ' . $args['network_id'] );
78 | } elseif ( is_wp_error( $network ) ) {
79 | $this->_debug(
80 | 'Network generation failed with message [%s] %s',
81 | $network->get_error_code(),
82 | $network->get_error_messages()
83 | );
84 | } else {
85 | $this->_debug( 'Network generation failed' );
86 | }
87 |
88 | return $args['network_id'];
89 | }
90 |
91 | /**
92 | * Does nothing, just implements abstract method.
93 | *
94 | * @since 1.0.0
95 | *
96 | * @access protected
97 | * @param mixed $object The network id.
98 | * @param array $fields The array of fields to update.
99 | * @return int The network id.
100 | */
101 | protected function _updateObject( $network_id, $fields ) {
102 | return $network_id;
103 | }
104 |
105 | /**
106 | * Deletes previously generated network.
107 | *
108 | * @since 1.0.0
109 | *
110 | * @access protected
111 | * @global \wpdb $wpdb The database connection.
112 | * @param int $network_id The network id.
113 | * @return boolean TRUE on success, otherwise FALSE.
114 | */
115 | protected function _deleteObject( $network_id ) {
116 | global $wpdb;
117 |
118 | $network_blog = wp_get_sites( array( 'network_id' => $network_id ) );
119 | if ( ! empty( $network_blog ) ) {
120 | $suppress = $wpdb->suppress_errors();
121 |
122 | foreach ( $network_blog as $blog ) {
123 | wpmu_delete_blog( $blog->blog_id, true );
124 | }
125 |
126 | $wpdb->suppress_errors( $suppress );
127 | }
128 |
129 | $deleted = $wpdb->delete( $wpdb->site, array( 'id' => $network_id ), array( '%d' ) );
130 | if ( $deleted ) {
131 | $wpdb->delete( $wpdb->sitemeta, array( 'site_id' => $network_id ), array( '%d' ) );
132 | $this->_debug( 'Deleted network with ID: ' . $network_id );
133 |
134 | return true;
135 | }
136 |
137 | $this->_debug( 'Failed to delet network with ID: ' . $network_id );
138 |
139 | return false;
140 | }
141 |
142 | /**
143 | * Returns generated network by id.
144 | *
145 | * @since 1.0.0
146 | *
147 | * @access public
148 | * @param int $network_id The network id.
149 | * @return object|boolean The generated nework on success, otherwise FALSE.
150 | */
151 | public function getObjectById( $network_id ) {
152 | return wp_get_network( $network_id );
153 | }
154 |
155 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Factory/Comment.php:
--------------------------------------------------------------------------------
1 | new FakerSequence( 'name' ),
45 | 'comment_author_url' => new FakerSequence( 'url' ),
46 | 'comment_approved' => 1,
47 | 'comment_content' => new FakerSequence( 'sentences' ),
48 | ) );
49 | }
50 |
51 | /**
52 | * Generates a new comment.
53 | *
54 | * @since 1.0.0
55 | *
56 | * @access protected
57 | * @param array $args The array of arguments to use during a new comment creation.
58 | * @return int|boolean The newly created comment's ID on success, otherwise FALSE.
59 | */
60 | protected function _createObject( $args ) {
61 | $comment_id = wp_insert_comment( $this->_addSlashesDeep( $args ) );
62 | if ( $comment_id && ! is_wp_error( $comment_id ) ) {
63 | $this->_debug( 'Generated comment ID: ' . $comment_id );
64 | } elseif ( is_wp_error( $comment_id ) ) {
65 | $this->_debug(
66 | 'Comment generation failed with message [%s] %s',
67 | $comment_id->get_error_code(),
68 | $comment_id->get_error_messages()
69 | );
70 | } else {
71 | $this->_debug( 'Comment generation failed' );
72 | }
73 |
74 | return $comment_id;
75 | }
76 |
77 | /**
78 | * Updates generated comment.
79 | *
80 | * @since 1.0.0
81 | *
82 | * @access protected
83 | * @param mixed $comment_id The comment id.
84 | * @param array $fields The array of fields to update.
85 | * @return boolean TRUE on success, otherwise FALSE.
86 | */
87 | protected function _updateObject( $comment_id, $fields ) {
88 | $fields['comment_ID'] = $comment_id;
89 | $updated = (bool) wp_update_comment( $this->_addSlashesDeep( $fields ) );
90 | if ( $updated ) {
91 | $this->_debug( 'Updated comment ' . $comment_id );
92 | } else {
93 | $this->_debug( 'Update failed for comment ' . $comment_id );
94 | }
95 |
96 | return $updated;
97 | }
98 |
99 | /**
100 | * Deletes previously generated comment.
101 | *
102 | * @since 1.0.0
103 | *
104 | * @access protected
105 | * @param int $comment_id The comment id.
106 | * @return boolean TRUE on success, otherwise FALSE.
107 | */
108 | protected function _deleteObject( $comment_id ) {
109 | $comment = get_comment( $comment_id );
110 | if ( ! $comment || is_wp_error( $comment ) ) {
111 | return false;
112 | }
113 |
114 | $deleted = wp_delete_comment( $comment_id, true );
115 | if ( $deleted ) {
116 | $this->_debug( 'Deleted comment with ID: ' . $comment_id );
117 | return true;
118 | }
119 |
120 | $this->_debug( 'Comment removal failed for ID: ' . $comment_id );
121 | return false;
122 | }
123 |
124 | /**
125 | * Creates comments for a post.
126 | *
127 | * @since 1.0.0
128 | *
129 | * @access public
130 | * @param int $post_id The post id to create comments for.
131 | * @param int $count The number of comments to create.
132 | * @param array $args The array of arguments to use during a new object creation.
133 | * @param array $definitions Custom difinitions of default values for a new object properties.
134 | * @return array The array of generated comment ids.
135 | */
136 | public function createPostComments( $post_id, $count = 1, $args = array(), $definitions = null ) {
137 | $args['comment_post_ID'] = $post_id;
138 | return $this->createMany( $count, $args, $definitions );
139 | }
140 |
141 | /**
142 | * Returns generated comment by id.
143 | *
144 | * @since 1.0.0
145 | *
146 | * @access public
147 | * @param int $comment_id The comment id.
148 | * @return mixed The generated comment on success, otherwise NULL.
149 | */
150 | public function getObjectById( $comment_id ) {
151 | return get_comment( $comment_id );
152 | }
153 |
154 | }
--------------------------------------------------------------------------------
/wp-codeception.php:
--------------------------------------------------------------------------------
1 | getMessage() );
57 | }
58 | }
59 |
60 | // Register WP-CLI commands
61 | WP_CLI::add_command( 'codeception', '\WPCC\CLI\Codeception' );
62 | WP_CLI::add_command( 'selenium', '\WPCC\CLI\Selenium' );
63 |
64 | }
65 |
66 | /**
67 | * Following functions were copied from the Codeception's autoload file, because
68 | * we are not going to load it anymore, but these functions are still required
69 | * and used in the depths of the framework, so we have to load it here.
70 | */
71 |
72 | if ( ! function_exists( 'codecept_debug' ) ) :
73 |
74 | /**
75 | * Registers debug message which will be printed if --debug argument is passed to the command.
76 | *
77 | * @since 1.0.1
78 | *
79 | * @param mixed $data The debug data, it will be serialized if we need to display it.
80 | */
81 | function codecept_debug( $data ) {
82 | \Codeception\Util\Debug::debug( $data );
83 | }
84 |
85 | endif;
86 |
87 | if ( ! function_exists( 'codecept_root_dir' ) ) :
88 |
89 | /**
90 | * Returns absolute path to the requested object which is expected to be in
91 | * the root directory of a testing project.
92 | *
93 | * @since 1.0.1
94 | *
95 | * @param string $appendPath A relative path to the requested object.
96 | * @return string The absolute path to the object.
97 | */
98 | function codecept_root_dir( $appendPath = '' ) {
99 | return \Codeception\Configuration::projectDir() . $appendPath;
100 | }
101 |
102 | endif;
103 |
104 | if ( ! function_exists( 'codecept_output_dir' ) ) :
105 |
106 | /**
107 | * Returns absolute path to the requested object which is expected to be in
108 | * the output directory of a testing project.
109 | *
110 | * @since 1.0.1
111 | *
112 | * @param string $appendPath A relative path to the requested object.
113 | * @return string The absolute path to the object.
114 | */
115 | function codecept_output_dir( $appendPath = '' ) {
116 | return \Codeception\Configuration::outputDir() . $appendPath;
117 | }
118 |
119 | endif;
120 |
121 | if ( ! function_exists( 'codecept_log_dir' ) ) :
122 |
123 | /**
124 | * Returns absolute path to the requested object which is expected to be in
125 | * the log directory of a testing project.
126 | *
127 | * @since 1.0.1
128 | *
129 | * @param string $appendPath A relative path to the requested object.
130 | * @return string The absolute path to the object.
131 | */
132 | function codecept_log_dir( $appendPath = '' ) {
133 | return \Codeception\Configuration::outputDir() . $appendPath;
134 | }
135 |
136 | endif;
137 |
138 | if ( ! function_exists( 'codecept_data_dir' ) ) :
139 |
140 | /**
141 | * Returns absolute path to the requested object which is expected to be in
142 | * the data directory of a testing project.
143 | *
144 | * @since 1.0.1
145 | *
146 | * @param string $appendPath A relative path to the requested object.
147 | * @return string The absolute path to the object.
148 | */
149 | function codecept_data_dir( $appendPath = '' ) {
150 | return \Codeception\Configuration::dataDir() . $appendPath;
151 | }
152 |
153 | endif;
154 |
--------------------------------------------------------------------------------
/classes/WPCC/Command/Run.php:
--------------------------------------------------------------------------------
1 | options = $input->getOptions();
49 | $this->output = $output;
50 |
51 | $config = Configuration::config( $this->options['config'] );
52 |
53 | if ( ! $this->options['colors'] ) {
54 | $this->options['colors'] = $config['settings']['colors'];
55 | }
56 |
57 | if ( ! $this->options['silent'] ) {
58 | $this->output->writeln( Codecept::versionString() . "\nPowered by " . \PHPUnit_Runner_Version::getVersionString() );
59 | }
60 |
61 | if ( $this->options['debug'] ) {
62 | $this->output->setVerbosity( OutputInterface::VERBOSITY_VERY_VERBOSE );
63 | }
64 |
65 | $userOptions = array_intersect_key( $this->options, array_flip( $this->passedOptionKeys( $input ) ) );
66 | $userOptions = array_merge( $userOptions, $this->booleanOptions( $input, ['xml', 'html', 'json', 'tap', 'coverage', 'coverage-xml', 'coverage-html' ] ) );
67 | $userOptions['verbosity'] = $this->output->getVerbosity();
68 | $userOptions['interactive'] = ! $input->hasParameterOption( array( '--no-interaction', '-n' ) );
69 |
70 | if ( $this->options['no-colors'] ) {
71 | $userOptions['colors'] = false;
72 | }
73 | if ( $this->options['group'] ) {
74 | $userOptions['groups'] = $this->options['group'];
75 | }
76 | if ( $this->options['skip-group'] ) {
77 | $userOptions['excludeGroups'] = $this->options['skip-group'];
78 | }
79 | if ( $this->options['report'] ) {
80 | $userOptions['silent'] = true;
81 | }
82 | if ( $this->options['coverage-xml'] || $this->options['coverage-html'] || $this->options['coverage-text'] ) {
83 | $this->options['coverage'] = true;
84 | }
85 |
86 | $suite = $input->getArgument( 'suite' );
87 | $test = $input->getArgument( 'test' );
88 |
89 | if ( ! Configuration::isEmpty() && ! $test && strpos( $suite, $config['paths']['tests'] ) === 0 ) {
90 | list( $matches, $suite, $test ) = $this->matchTestFromFilename( $suite, $config['paths']['tests'] );
91 | }
92 |
93 | if ( $this->options['group'] ) {
94 | $this->output->writeln( sprintf( "[Groups] %s ", implode( ', ', $this->options['group'] ) ) );
95 | }
96 | if ( $input->getArgument( 'test' ) ) {
97 | $this->options['steps'] = true;
98 | }
99 |
100 | if ( $test ) {
101 | $filter = $this->matchFilteredTestNameEx( $test );
102 | $userOptions['filter'] = $filter;
103 | }
104 |
105 | $this->codecept = new Codecept( $userOptions );
106 |
107 | if ( $suite && $test ) {
108 | $this->codecept->run( $suite, $test );
109 | }
110 |
111 | if ( ! $test ) {
112 | $suites = $suite ? explode( ',', $suite ) : Configuration::suites();
113 | $this->executed = $this->runSuites( $suites, $this->options['skip'] );
114 |
115 | if ( !empty( $config['include'] ) ) {
116 | $current_dir = Configuration::projectDir();
117 | $suites += $config['include'];
118 | $this->runIncludedSuites( $config['include'], $current_dir );
119 | }
120 |
121 | if ( $this->executed === 0 ) {
122 | throw new \RuntimeException(
123 | sprintf( "Suite '%s' could not be found", implode( ', ', $suites ) )
124 | );
125 | }
126 | }
127 |
128 | $this->codecept->printResult();
129 |
130 | if ( ! $input->getOption( 'no-exit' ) ) {
131 | if ( ! $this->codecept->getResult()->wasSuccessful() ) {
132 | exit( 1 );
133 | }
134 | }
135 | }
136 |
137 | /**
138 | * Overriden version of private function.
139 | *
140 | * @since 1.0.0
141 | *
142 | * @access protected
143 | * @param string $path The test name.
144 | * @return string The filtered test name on success, otherwise NULL.
145 | */
146 | protected function matchFilteredTestNameEx( &$path ) {
147 | $test_parts = explode( ':', $path );
148 | if ( count( $test_parts ) > 1 ) {
149 | list( $path, $filter ) = $test_parts;
150 | return $filter;
151 | }
152 |
153 | return null;
154 | }
155 |
156 | }
--------------------------------------------------------------------------------
/classes/WPCC/Module/Wpdb.php:
--------------------------------------------------------------------------------
1 | _wpdb = $wpdb;
55 | }
56 |
57 | /**
58 | * Builds query.
59 | *
60 | * @since 1.0.0
61 | *
62 | * @access protected
63 | * @param string $table The table name.
64 | * @param array|string $columns The array of columns to select.
65 | * @param array $criteria The array of conditions.
66 | * @return string The query string.
67 | */
68 | protected function _prepareQuery( $table, $columns, $criteria ) {
69 | $where = '1 = 1';
70 | $params = array();
71 |
72 | foreach ( $criteria as $column => $value ) {
73 | $pattern = '%s';
74 | if ( is_null( $value ) ) {
75 | $pattern = '%s AND `%s` IS NULL';
76 | } elseif ( is_numeric( $value ) ) {
77 | $pattern = '%s AND `%s` = %%d';
78 | $params[] = $value;
79 | } else {
80 | $pattern = '%s AND `%s` = %%s';
81 | $params[] = $value;
82 | }
83 |
84 | $where = sprintf( $pattern, $where, $column );
85 | }
86 |
87 | if ( is_array( $columns ) ) {
88 | $columns = implode( ', ', $columns );
89 | }
90 |
91 | $query = sprintf( 'SELECT %s FROM %s WHERE %s', $columns, $table, $where );
92 | if ( ! empty( $params ) ) {
93 | $query = $this->_wpdb->prepare( $query, $params );
94 | }
95 |
96 | return $query;
97 | }
98 |
99 | /**
100 | * Checks whether or not a record exists in the database.
101 | *
102 | * @since 1.0.0
103 | *
104 | * @access public
105 | * @param string $table The table name.
106 | * @param array $criteria The array of conditions.
107 | */
108 | public function seeInDatabase( $table, $criteria = array() ) {
109 | $query = $this->_prepareQuery( $table, 'count(*)', $criteria );
110 | $this->debugSection( 'Query', $query );
111 |
112 | $suppress_errors = $this->_wpdb->suppress_errors( true );
113 | $res = $this->_wpdb->get_var( $query );
114 | $this->_wpdb->suppress_errors( $suppress_errors );
115 |
116 | if ( ! empty( $this->_wpdb->last_error ) ) {
117 | $this->fail( 'Database error: ' . $this->_wpdb->last_error );
118 | return;
119 | }
120 |
121 | $this->assertGreaterThan( 0, $res, 'No matching records found' );
122 | }
123 |
124 | /**
125 | * Checks whether or not a record doesn't exist in the database.
126 | *
127 | * @since 1.0.0
128 | *
129 | * @access public
130 | * @param string $table The table name.
131 | * @param array $criteria The array of conditions.
132 | */
133 | public function dontSeeInDatabase( $table, $criteria = array() ) {
134 | $query = $this->_prepareQuery( $table, 'count(*)', $criteria );
135 | $this->debugSection( 'Query', $query );
136 |
137 | $suppress_errors = $this->_wpdb->suppress_errors( true );
138 | $res = $this->_wpdb->get_var( $query );
139 | $this->_wpdb->suppress_errors( $suppress_errors );
140 |
141 | if ( ! empty( $this->_wpdb->last_error ) ) {
142 | $this->fail( 'Database error: ' . $this->_wpdb->last_error );
143 | return;
144 | }
145 |
146 | $this->assertLessThan( 1, $res, 'Matching records found' );
147 | }
148 |
149 | /**
150 | * Fetches rows from database.
151 | *
152 | * @since 1.0.0
153 | *
154 | * @access public
155 | * @param string $table The table name.
156 | * @param array|string $columns The array of columns to select.
157 | * @param array $criteria The array of conditions.
158 | * @return array The array of fetched rows.
159 | */
160 | public function grabFromDatabase( $table, $columns, $criteria = array() ) {
161 | $query = $this->_prepareQuery( $table, $columns, $criteria );
162 | $this->debugSection( 'Query', $query );
163 |
164 | $suppress_errors = $this->_wpdb->suppress_errors( true );
165 | $results = $this->_wpdb->get_results( $query );
166 | $this->_wpdb->suppress_errors( $suppress_errors );
167 |
168 | if ( ! empty( $this->_wpdb->last_error ) ) {
169 | $this->fail( 'Database error: ' . $this->_wpdb->last_error );
170 | return;
171 | }
172 |
173 | return $results;
174 | }
175 |
176 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Factory/Term.php:
--------------------------------------------------------------------------------
1 | _taxonomy = $taxonomy ?: self::DEFAULT_TAXONOMY;
58 |
59 | parent::__construct( array(
60 | 'name' => new Sequence( 'Term %s' ),
61 | 'taxonomy' => $this->_taxonomy,
62 | 'description' => new FakerSequence( 'paragraph' ),
63 | ) );
64 | }
65 |
66 | /**
67 | * Generates a new term.
68 | *
69 | * @since 1.0.0
70 | *
71 | * @access protected
72 | * @param array $args The array of arguments to use during a new term creation.
73 | * @return int|\WP_Error The newly created term's ID on success, otherwise a WP_Error object.
74 | */
75 | protected function _createObject( $args ) {
76 | $args = array_merge( array( 'taxonomy' => $this->_taxonomy ), $args );
77 | $term_id_pair = wp_insert_term( $args['name'], $args['taxonomy'], $args );
78 | if ( is_wp_error( $term_id_pair ) ) {
79 | $this->_debug(
80 | 'Term generation failed with message [%s] %s',
81 | $term_id_pair->get_error_code(),
82 | $term_id_pair->get_error_messages()
83 | );
84 |
85 | return $term_id_pair;
86 | }
87 |
88 | $this->_debug( 'Generated term ID: ' . $term_id_pair['term_id'] );
89 |
90 | return $term_id_pair['term_id'];
91 | }
92 |
93 | /**
94 | * Updates generated term.
95 | *
96 | * @since 1.0.0
97 | *
98 | * @access protected
99 | * @param mixed $term The term id to update.
100 | * @param array $fields The array of fields to update.
101 | * @return mixed Updated term ID on success, otherwise a WP_Error object.
102 | */
103 | protected function _updateObject( $term, $fields ) {
104 | $fields = array_merge( array( 'taxonomy' => $this->_taxonomy ), $fields );
105 | $taxonomy = is_object( $term ) ? $term->taxonomy : $this->_taxonomy;
106 |
107 | $term_id_pair = wp_update_term( $term, $taxonomy, $fields );
108 | if ( is_wp_error( $term_id_pair ) ) {
109 | $this->_debug(
110 | 'Update failed for term %d with message [%s] %s',
111 | is_object( $term ) ? $term->term_id : $term,
112 | $term_id_pair->get_error_code(),
113 | $term_id_pair->get_error_message()
114 | );
115 |
116 | return $term_id_pair;
117 | }
118 |
119 | $this->_debug( 'Updated term ' . $term_id_pair['term_id'] );
120 |
121 | return $term_id_pair['term_id'];
122 | }
123 |
124 | /**
125 | * Deletes previously generated term.
126 | *
127 | * @since 1.0.0
128 | *
129 | * @access protected
130 | * @param int $term_id The term id to delete.
131 | * @return boolean TRUE on success, otherwise FALSE.
132 | */
133 | protected function _deleteObject( $term_id ) {
134 | $term = get_term_by( 'id', $term_id, $this->_taxonomy );
135 | if ( ! $term || is_wp_error( $term ) ) {
136 | return false;
137 | }
138 |
139 | $deleted = wp_delete_term( $term_id, $this->_taxonomy );
140 | if ( $deleted && ! is_wp_error( $deleted ) ) {
141 | $this->_debug( 'Deleted term with ID: ' . $term_id );
142 | return true;
143 | } elseif ( is_wp_error( $deleted ) ) {
144 | $this->_debug(
145 | 'Removal failed for term %d with message [%s] %s',
146 | $term_id,
147 | $deleted->get_error_code(),
148 | $deleted->get_error_message()
149 | );
150 | } else {
151 | $this->_debug( 'Term removal failed for ID: ' . $term_id );
152 | }
153 |
154 | return false;
155 | }
156 |
157 | /**
158 | * Adds terms to a post.
159 | *
160 | * @since 1.0.0
161 | *
162 | * @access public
163 | * @param int $post_id The post id to add terms to.
164 | * @param array $terms The array of terms to add.
165 | * @param string $taxonomy The taxonomy name of terms.
166 | * @param boolean $append Determines whether to add or replace terms.
167 | * @return array The array of affected term IDs.
168 | */
169 | public function addPostTerms( $post_id, $terms, $taxonomy, $append = true ) {
170 | return wp_set_post_terms( $post_id, $terms, $taxonomy, $append );
171 | }
172 |
173 | /**
174 | * Returns generated term by id.
175 | *
176 | * @since 1.0.0
177 | *
178 | * @access public
179 | * @param int $term_id The term id.
180 | * @return mixed The generated term on success, otherwise NULL or a WP_Error object.
181 | */
182 | public function getObjectById( $term_id ) {
183 | return get_term( $term_id, $this->_taxonomy );
184 | }
185 |
186 | }
187 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WP Codeception
2 |
3 | > This is a WordPress Plugin which integrates with the [Codeception](http://codeception.com/) PHP testing framework and allows you to write and run Codeception tests for WordPress via WP CLI.
4 |
5 | [](#support-level)
6 |
7 | We're working towards supporting all of [Codeceptions commands](http://codeception.com/docs/reference/Commands). If you find one we haven't included yet, please submit a [Pull Request](https://github.com/10up/wp-codeception/pulls)!
8 |
9 | ## Installation
10 | [Download the latest version](https://github.com/10up/wp-codeception/archive/master.zip) and extract, or clone the repository with Git into a new directory `wp-content/plugins/wp-codeception` in your WordPress install.
11 |
12 | #### Install required node modules and composer dependencies
13 |
14 | We'll run our commands from within [VVV](https://github.com/Varying-Vagrant-Vagrants/VVV) because WP CLI, Node, and Composer are already installed for us there.
15 |
16 | ```Bash
17 | $ vagrant up
18 | $ vagrant ssh
19 | $ sudo apt-get install openjdk-7-jre-headless
20 | $ cd /srv/www/yoursite/htdocs/wp-content/plugins/wp-codeception
21 | $ composer install
22 | $ wp plugin activate wp-codeception
23 | ```
24 |
25 | Afterwards you'll have a new `vendor` directory within your `plugins/wp-codeception` directory which contains all the code libraries we're dependant on.
26 |
27 | #### Install as a composer dependency
28 |
29 | There is an alternative way to install this plugin. You can add it as a composer dependency for your project. To do it, run following command in your terminal:
30 |
31 | ```Bash
32 | $ composer require 10up/wp-codeception
33 | ```
34 |
35 | This command will install the plugin and all its dependencies for your project. Please, pay attention that if you already use [composer/installers](https://github.com/composer/installers) dependency in your project, then `wp-codeception` will be installed into `/wp-content/plugins/wp-codeception/` folder. It happens, because `wp-codeception` has `wordpress-plugin` type and will be processed by `composer/installers` accordingly (read its documentation for more details).
36 |
37 | If you want to add it as a dependency to your plugin or theme, you will need to update your `composer.json` file and tell it where to install `wp-codeception`. You can achieve it by providing `installer-paths` instructions like in the snippet below.
38 |
39 | ```Bash
40 | {
41 | ...,
42 | "extra": {
43 | "installer-paths": {
44 | "vendor/{$name}/": ["type:wordpress-plugin"]
45 | }
46 | },
47 | ...
48 | }
49 | ```
50 |
51 | Now `composer/installers` will know to install wordpress plugins into *vendor* folder. The final step which you need to do is to update your `autoload` section and add `wp-codeception.php` file to the autoload files list.
52 |
53 | ```Bash
54 | {
55 | ...,
56 | "autoload": {
57 | "psr-X": {
58 | ...
59 | },
60 | "files": [
61 | ...,
62 | "vendor/wp-codeception/wp-codeception.php"
63 | ]
64 | },
65 | ...
66 | }
67 | ```
68 |
69 | #### Install the test suite
70 |
71 | See the [Codeception bootstrapping documentation](http://codeception.com/docs/reference/Commands#Bootstrap) for further information.
72 |
73 | ```Bash
74 | # You'll create the test suite in your own plugin or theme directory..
75 | $ cd /srv/www/yoursite/htdocs/wp-content/{your plugin or theme directory}
76 | $ wp codeception bootstrap
77 | ```
78 |
79 | Afterwards you'll have a new `tests` directory within your plugin or theme directory. This is your new test suite, and where you'll write all your tests.
80 |
81 | ## Writing Tests
82 | You can write tests using any of the three [Codeception](http://codeception.com/) testing frameworks: [Acceptance](http://codeception.com/docs/03-AcceptanceTests), [Functional](http://codeception.com/docs/04-FunctionalTests) and [Unit](http://codeception.com/docs/05-UnitTests) testing. If you look within the new `tests` directory you'll see three config files; one for each test framework (acceptance.suite.yml, functional.suite.yml, unit.suite.yml). Edit these files as you wish.
83 |
84 | #### Generate your first test
85 | ```Bash
86 | # You should be in the plugin or theme directory where you ran the bootstrap
87 | $ wp codeception generate-(cept|cest) (acceptance|functional|unit) MyTestName
88 |
89 | # Example
90 | $ wp codeception generate-cept acceptance LoginTest
91 | ```
92 |
93 | Afterwards you'll have a new file in your plugin or theme directory `tests/acceptance/LoginTest.php`, where you can write your first test. Remember, any Codeception test will work here! For example, you could run any of the [acceptance test examples](http://codeception.com/docs/03-AcceptanceTests) mentioned in the Codeception documentation. Likewise, the same goes for [Functional](http://codeception.com/docs/04-FunctionalTests) and [Unit tests](http://codeception.com/docs/05-UnitTests).
94 |
95 | #### Example: Writing a Login Acceptance Test
96 | ```PHP
97 | wantTo( 'Ensure WordPress Login Works' );
103 |
104 | // Let's start on the login page
105 | $I->amOnPage( wp_login_url() );
106 |
107 | // Populate the login form's user id field
108 | $I->fillField( 'input#user_login', 'YourUsername' );
109 |
110 | // Popupate the login form's password field
111 | $I->fillField( 'input#user_pass', 'YourPassword' );
112 |
113 | // Submit the login form
114 | $I->click( 'Log In' );
115 |
116 | // Validate the successful loading of the Dashboard
117 | $I->see( 'Dashboard' );
118 | ```
119 |
120 | ## Running Your Tests
121 | Now you've written some tests, it's time to run them! But first..
122 |
123 | #### Selenium
124 | If you've created any browser automation/acceptance tests you'll need to turn [Selenium](http://www.seleniumhq.org/) on, and likewise, you'll want to stop Selenium after you're through running tests.
125 |
126 | ```Bash
127 | # You can run these commands from anywhere in your WordPress install
128 | $ wp selenium start
129 |
130 | # Stop Selenium when you're through
131 | $ wp selenium stop
132 | ```
133 |
134 | #### Run
135 | You'll use the `run` command to execute your tests from within your plugin or theme directory (where you ran the bootstrap). We've implemented most of the [Codeception 'run' command](http://codeception.com/docs/reference/Commands#Run) arguments, but if you find one we've missed please submit a [Pull Request](https://github.com/10up/wp-codeception/pulls)!
136 |
137 | ```Bash
138 | # You should be in the plugin or theme directory where you ran the bootstrap
139 | $ wp codeception run
140 | ```
141 |
142 | #### Example: Running our Login Test
143 | ```Bash
144 | # You should be in the plugin or theme directory where you ran the bootstrap
145 | # Let's display verbose output
146 | $ wp codeception run -vvv
147 |
148 | Codeception PHP Testing Framework v2.0.11
149 | Powered by PHPUnit 4.5.1 by Sebastian Bergmann and contributors.
150 |
151 | Rebuilding AcceptanceTester...
152 |
153 | Acceptance-production Tests (1) ---------------------------------
154 | Modules: WebDriver, WordPress, AcceptanceHelper
155 | -----------------------------------------------------------------
156 | Ensure WordPress Login Works (LoginTest)
157 | Scenario:
158 | * I am on page "http://site.com/wp-login.php"
159 | * I fill field "input#user_login","YourUsername"
160 | * I fill field "input#user_pass","YourPassword"
161 | * I click "Login"
162 | * I see "Dashboard"
163 | PASSED
164 | ```
165 |
166 | ## Support Level
167 |
168 | **Archived:** This project is no longer maintained by 10up. We are no longer responding to Issues or Pull Requests unless they relate to security concerns. We encourage interested developers to fork this project and make it their own!
169 |
170 | ## Like what you see?
171 |
172 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/classes/WPCC/Module/MailtrapIO.php:
--------------------------------------------------------------------------------
1 | '2525',
59 | );
60 |
61 | /**
62 | * The current email data.
63 | *
64 | * @since 1.0.0
65 | *
66 | * @access protected
67 | * @var array
68 | */
69 | protected $_current_email = array();
70 |
71 | /**
72 | * Setups module environment.
73 | *
74 | * @since 1.0.0
75 | *
76 | * @access public
77 | */
78 | public function _initialize() {
79 | add_action( 'phpmailer_init', array( $this, '_setup_smtp_settings' ) );
80 | }
81 |
82 | /**
83 | * Setups SMPT settings for the mailer object.
84 | *
85 | * @since 1.0.0
86 | * @action phpmailer_init
87 | *
88 | * @access public
89 | * @param \PHPMailer $phpmailer The mailer object.
90 | */
91 | public function _setup_smtp_settings( \PHPMailer $phpmailer ) {
92 | $phpmailer->Host = 'mailtrap.io';
93 | $phpmailer->Port = $this->config['port'];
94 | $phpmailer->Username = $this->config['username'];
95 | $phpmailer->Password = $this->config['password'];
96 | $phpmailer->SMTPAuth = true;
97 | $phpmailer->SMTPDebug = 1;
98 |
99 | $phpmailer->IsSMTP();
100 | }
101 |
102 | /**
103 | * Sends request to the Mailtrap.io API.
104 | *
105 | * @since 1.0.0
106 | *
107 | * @access protected
108 | * @param string $url The relative URL to a resource.
109 | * @param array $args The array of arguments for a request.
110 | * @return array The response array.
111 | */
112 | protected function _send_request( $url, $args = array() ) {
113 | $url = 'https://mailtrap.io/api/v1/inboxes/' . $this->config['inbox_id'] . $url;
114 |
115 | $args = wp_parse_args( $args, array(
116 | 'method' => 'GET',
117 | 'headers' => array(),
118 | ) );
119 |
120 | $args['headers'] = array_merge( $args['headers'], array(
121 | 'Api-Token' => $this->config['api_token'],
122 | 'Accept' => 'application/json',
123 | ) );
124 |
125 | $response = wp_remote_request( $url, $args );
126 |
127 | return $response;
128 | }
129 |
130 | /**
131 | * Sends GET request to the Mailtrap.io API.
132 | *
133 | * @since 1.0.0
134 | *
135 | * @access public
136 | * @param string $url The relative URL to a resource.
137 | * @return array The response array.
138 | */
139 | protected function _send_get_request( $url ) {
140 | $response = $this->_send_request( $url, array( 'method' => 'GET' ) );
141 |
142 | $response_code = wp_remote_retrieve_response_code( $response );
143 | $this->assertEquals( 200, $response_code, 'The Mailtrap.io API resonse code is not equals to 200.' );
144 |
145 | return $response;
146 | }
147 |
148 | /**
149 | * Sends DELETE request to the Mailtrap.io API.
150 | *
151 | * @since 1.0.0
152 | *
153 | * @access protected
154 | * @param string $url The relative URL to a resource.
155 | * @return array The response array.
156 | */
157 | protected function _send_delete_request( $url ) {
158 | $response = $this->_send_request( $url, array( 'method' => 'DELETE' ) );
159 |
160 | $response_code = wp_remote_retrieve_response_code( $response );
161 | $this->assertEquals( 200, $response_code, 'The Mailtrap.io API resonse code is not equals to 200.' );
162 |
163 | return $response;
164 | }
165 |
166 | /**
167 | * Sends PATCH request to the Mailtrap.io API.
168 | *
169 | * @since 1.0.0
170 | *
171 | * @access protected
172 | * @param string $url The relative URL to a resource.
173 | * @param array $body The request body.
174 | * @return array The response array.
175 | */
176 | protected function _send_patch_request( $url, $body ) {
177 | $response = $this->_send_request( $url, array(
178 | 'method' => 'PATCH',
179 | 'body' => $body,
180 | ) );
181 |
182 | $response_code = wp_remote_retrieve_response_code( $response );
183 | $this->assertEquals( 200, $response_code, 'The Mailtrap.io API resonse code is not equals to 200.' );
184 |
185 | return $response;
186 | }
187 |
188 | /**
189 | * Checks whether a new email exists for a recipient or not.
190 | *
191 | * @since 1.0.0
192 | *
193 | * @access public
194 | * @param string $recipient The email address of a recipient.
195 | */
196 | public function seeNewEmailFor( $recipient ) {
197 | $email = $this->grabLatestEmailFor( $recipient );
198 | $this->assertEquals( $recipient, $email['to_email'], 'The email recipient is wrong.' );
199 | $this->assertFalse( $email['is_read'], 'The email is already read.' );
200 | }
201 |
202 | /**
203 | * Grabs latest email for a recipient.
204 | *
205 | * @since 1.0.0
206 | *
207 | * @access public
208 | * @param string $recipient The email address of a recipient.
209 | * @return array The email array.
210 | */
211 | public function grabLatestEmailFor( $recipient ) {
212 | $response = $this->_send_get_request( '/messages?search=' . urlencode( $recipient ) );
213 |
214 | $emails = json_decode( wp_remote_retrieve_body( $response ), true );
215 | $this->assertNotEmpty( $emails, 'Received emails array is empty.' );
216 |
217 | $email = current( $emails );
218 |
219 | return $email;
220 | }
221 |
222 | /**
223 | * Deletes an email from the inbox. If no email id is provided, then email id
224 | * will be taken from the current email.
225 | *
226 | * @since 1.0.0
227 | *
228 | * @access public
229 | * @param int $email_id The email id to delete.
230 | */
231 | public function deleteEmail( $email_id = null ) {
232 | if ( ! $email_id ) {
233 | $this->assertNotEmpty( $this->_current_email, 'The current email is not selected' );
234 | $email_id = $this->_current_email['id'];
235 | }
236 |
237 | $this->_send_delete_request( '/messages/' . $email_id );
238 | }
239 |
240 | /**
241 | * Marks email read. If no email id is provided, then email id will be taken
242 | * from the current email.
243 | *
244 | * @since 1.0.0
245 | *
246 | * @access public
247 | * @param int $email_id The email id.
248 | */
249 | public function markEmailRead( $email_id = null ) {
250 | if ( ! $email_id ) {
251 | $this->assertNotEmpty( $this->_current_email, 'The current email is not selected' );
252 | $email_id = $this->_current_email['id'];
253 | }
254 |
255 | $this->_send_patch_request( '/messages/' . $email_id, array(
256 | 'message' => array( 'is_read' => true )
257 | ) );
258 | }
259 |
260 | /**
261 | * Selects latest email for a recipient.
262 | *
263 | * @since 1.0.0
264 | *
265 | * @access public
266 | * @param string $recipient The recipient email.
267 | */
268 | public function amOnLatestEmailFor( $recipient ) {
269 | $this->_current_email = $this->grabLatestEmailFor( $recipient );
270 | }
271 |
272 | /**
273 | * Checks whether email is not read yet or not.
274 | *
275 | * @since 1.0.0
276 | *
277 | * @access public
278 | */
279 | public function seeEmailIsNotRead() {
280 | $this->assertFalse( $this->_current_email['is_read'], 'The email is already read.' );
281 | }
282 |
283 | /**
284 | * Checks whether email is read or not.
285 | *
286 | * @since 1.0.0
287 | *
288 | * @access public
289 | */
290 | public function seeEmailIsRead() {
291 | $this->assertTrue( $this->_current_email['is_read'], 'The email is not read yet.' );
292 | }
293 |
294 | /**
295 | * Checks email subject to be equal to expected subject.
296 | *
297 | * @since 1.0.0
298 | *
299 | * @access public
300 | * @param string $subject The expected subject.
301 | */
302 | public function seeEmailSubjectIs( $subject ) {
303 | $this->assertEquals( $subject, $this->_current_email['subject'], 'The email subject is wrong.' );
304 | }
305 |
306 | /**
307 | * Checks whether sender email is equal to expected email.
308 | *
309 | * @since 1.0.0
310 | *
311 | * @access public
312 | * @param string $from_email The sender email address.
313 | */
314 | public function seeEmailIsFrom( $from_email ) {
315 | $this->assertEquals( $from_email, $this->_current_email['from_email'], 'Sender email is not equal to expected email.' );
316 | }
317 |
318 | /**
319 | * Checks whether recipient email is equal to expected email.
320 | *
321 | * @since 1.0.0
322 | *
323 | * @access public
324 | * @param string $to_email The recipient email address.
325 | */
326 | public function seeEmailIsFor( $to_email ) {
327 | $this->assertEquals( $to_email, $this->_current_email['to_email'], 'Recipient email is not equal to expected email.' );
328 | }
329 |
330 | }
--------------------------------------------------------------------------------
/classes/WPCC/Component/Factory.php:
--------------------------------------------------------------------------------
1 | _definitions = $definitions;
68 | }
69 |
70 | /**
71 | * Displays debug message.
72 | *
73 | * @since 1.0.2
74 | *
75 | * @access protected
76 | * @param string $message Debug message.
77 | * @param array $args Debug arguments.
78 | */
79 | protected function _debug( $message, $args = null ) {
80 | $message = '[Factory] ' . $message;
81 |
82 | if ( func_num_args() == 1 ) {
83 | Debug::debug( $message );
84 | } else {
85 | $args = array_slice( func_get_args(), 1 );
86 | Debug::debugf( $message, $args );
87 | }
88 | }
89 |
90 | /**
91 | * Generates a new object.
92 | *
93 | * @since 1.0.0
94 | *
95 | * @abstract
96 | * @access protected
97 | * @param array $args The array of arguments to use during a new object creation.
98 | * @return object|boolean|\WP_Error A new object on success, otherwise FALSE or instance of WP_Error.
99 | */
100 | protected abstract function _createObject( $args );
101 |
102 | /**
103 | * Updates generated object.
104 | *
105 | * @since 1.0.0
106 | *
107 | * @abstract
108 | * @access protected
109 | * @param mixed $object The generated object.
110 | * @param array $fields The array of fields to update.
111 | * @return mixed Updated object on success, otherwise FALSE, 0 or a WP_Error object.
112 | */
113 | protected abstract function _updateObject( $object, $fields );
114 |
115 | /**
116 | * Deletes an object.
117 | *
118 | * @since 1.0.0
119 | *
120 | * @abstract
121 | * @access protected
122 | * @param mixed $object The generated object.
123 | * @return boolean|\WP_Error TRUE on success, otherwise FALSE or WP_Error object.
124 | */
125 | protected abstract function _deleteObject( $object );
126 |
127 | /**
128 | * Returns generated object by id.
129 | *
130 | * @since 1.0.0
131 | *
132 | * @abstract
133 | * @access public
134 | * @param int $object_id The object id.
135 | * @return mixed The generated object.
136 | */
137 | public abstract function getObjectById( $object_id );
138 |
139 | /**
140 | * Creates a new object.
141 | *
142 | * @since 1.0.0
143 | *
144 | * @access public
145 | * @param array $args The array of arguments to use during a new object creation.
146 | * @param array $definitions Custom difinitions of default values for a new object properties.
147 | * @return object|boolean|\WP_Error A new object identifier on success, otherwise FALSE or a WP_Error object.
148 | */
149 | public function create( $args = array(), $definitions = null ) {
150 | if ( is_null( $definitions ) ) {
151 | $definitions = $this->_definitions;
152 | }
153 |
154 | $callbacks = array();
155 | $generated_args = $this->_generateArgs( $args, $definitions, $callbacks );
156 | if ( is_wp_error( $generated_args ) ) {
157 | return $generated_args;
158 | }
159 |
160 | $created = $this->_createObject( $generated_args );
161 | if ( ! $created || is_wp_error( $created ) ) {
162 | return $created;
163 | }
164 |
165 | if ( ! empty( $callbacks ) ) {
166 | $updated_fields = $this->_applyCallbacks( $callbacks, $created );
167 | $save_result = $this->_updateObject( $created, $updated_fields );
168 | if ( ! $save_result || is_wp_error( $save_result ) ) {
169 | return $save_result;
170 | }
171 | }
172 |
173 | if ( ! empty( $created ) && !is_wp_error( $created ) ) {
174 | $this->_objects[] = $created;
175 | }
176 |
177 | return $created;
178 | }
179 |
180 | /**
181 | * Creates a new object and returns it.
182 | *
183 | * @since 1.0.0
184 | *
185 | * @access public
186 | * @param array $args The array of arguments to use during a new object creation.
187 | * @param array $definitions Custom difinitions of default values for a new object properties.
188 | * @return object|boolean|\WP_Error A new object on success, otherwise FALSE or a WP_Error object.
189 | */
190 | public function createAndGet( $args = array(), $definitions = null ) {
191 | $object_id = $this->create( $args, $definitions );
192 | if ( ! $object_id || is_wp_error( $object_id ) ) {
193 | return $object_id;
194 | }
195 |
196 | return $this->getObjectById( $object_id );
197 | }
198 |
199 | /**
200 | * Creates many new objects and returns their ids.
201 | *
202 | * @since 1.0.0
203 | *
204 | * @access public
205 | * @param int $count The number of objects to created.
206 | * @param array $args The array of arguments to use during a new object creation.
207 | * @param array $definitions Custom difinitions of default values for a new object properties.
208 | * @return array The array of generated object ids.
209 | */
210 | public function createMany( $count, $args = array(), $definitions = null ) {
211 | $results = array();
212 | for ( $i = 0; $i < $count; $i++ ) {
213 | $results[] = $this->create( $args, $definitions );
214 | }
215 |
216 | return $results;
217 | }
218 |
219 | /**
220 | * Deletes an object generated by this factory instance.
221 | *
222 | * @since 1.0.0
223 | *
224 | * @access public
225 | * @param mixed $object The generated object.
226 | * @return boolean|\WP_Error TRUE on success, otherwise FALSE or WP_Error object.
227 | */
228 | public function delete( $object ) {
229 | // do nothing if an object was generated not by this factory
230 | if ( ! in_array( $object, $this->_objects ) ) {
231 | return false;
232 | }
233 |
234 | // delete object and remove it from the objects list
235 | $deleted = $this->_deleteObject( $object );
236 | if ( $deleted && ! is_wp_error( $deleted ) ) {
237 | $index = array_search( $object, $this->_objects );
238 | if ( false !== $index ) {
239 | unset( $this->_objects[ $index ] );
240 | }
241 | }
242 |
243 | return $deleted;
244 | }
245 |
246 | /**
247 | * Deletes all objects generated by this factory instance.
248 | *
249 | * @since 1.0.0
250 | *
251 | * @access public
252 | */
253 | public function deleteAll() {
254 | foreach ( $this->_objects as $object ) {
255 | $this->delete( $object );
256 | }
257 | }
258 |
259 | /**
260 | * Generates arguments for a new object.
261 | *
262 | * @since 1.0.0
263 | *
264 | * @access protected
265 | * @param array $args The initial set of arguments.
266 | * @param array $definitions The definitions for auto generated properties.
267 | * @param array $callbacks The array of callbacks.
268 | * @return array|\WP_Error The array of arguments on success, otherwise a WP_Error object.
269 | */
270 | protected function _generateArgs( $args, $definitions, &$callbacks = array() ) {
271 | foreach ( $definitions as $field => $generator ) {
272 | if ( isset( $args[ $field ] ) ) {
273 | continue;
274 | }
275 |
276 | if ( is_scalar( $generator ) ) {
277 | $args[ $field ] = $generator;
278 | } elseif ( $generator instanceof AfterCreateCallback ) {
279 | $callbacks[ $field ] = $generator;
280 | } elseif ( $generator instanceof Sequence ) {
281 | $args[ $field ] = $generator->next();
282 | } else {
283 | return new \WP_Error( 'invalid_argument', 'Factory default value should be either a scalar or an generator object.' );
284 | }
285 | }
286 |
287 | return $args;
288 | }
289 |
290 | /**
291 | * Applies callbacks and returns updated fields.
292 | *
293 | * @since 1.0.0
294 | *
295 | * @access protected
296 | * @param array $callbacks The array of callbacks to call.
297 | * @param mixed $created The newly created object.
298 | * @return array The array of updated fields.
299 | */
300 | protected function _applyCallbacks( $callbacks, $created ) {
301 | $updated_fields = array();
302 | foreach ( $callbacks as $field => $callback ) {
303 | $updated_fields[ $field ] = $callback->call( $created );
304 | }
305 |
306 | return $updated_fields;
307 | }
308 |
309 | /**
310 | * Returns callback wrapper which will be called after a new object created.
311 | *
312 | * @since 1.0.0
313 | *
314 | * @access public
315 | * @param callable $function The callback to call after a new object created.
316 | * @return \WPCC\Component\Factory\Callback\AfterCreate
317 | */
318 | public function callback( $function ) {
319 | return new AfterCreateCallback( $function );
320 | }
321 |
322 | /**
323 | * Adds slashes recursively to each item of incomming value.
324 | *
325 | * @since 1.0.0
326 | *
327 | * @access protected
328 | * @param mixed $value The incomming value to add slashes to.
329 | * @return mixed Updated value with slashes.
330 | */
331 | protected function _addSlashesDeep( $value ) {
332 | if ( is_array( $value ) ) {
333 | $value = array_map( array( $this, '_addSlashesDeep' ), $value );
334 | } elseif ( is_object( $value ) ) {
335 | $vars = get_object_vars( $value );
336 | foreach ( $vars as $key => $data ) {
337 | $value->{$key} = $this->_addSlashesDeep( $data );
338 | }
339 | } elseif ( is_string( $value ) ) {
340 | $value = addslashes( $value );
341 | }
342 |
343 | return $value;
344 | }
345 |
346 | }
--------------------------------------------------------------------------------
/classes/WPCC/CLI/Codeception.php:
--------------------------------------------------------------------------------
1 |
46 | * : The suite name to run. There are three types of suites available to
47 | * use: unit, functional and acceptance, but currently only acceptance tests
48 | * are supported.
49 | *
50 | *
51 | * : The test name to run.
52 | *
53 | *
54 | * : Path to the custom config file.
55 | *
56 | *
57 | * : Determines whether to show output in compact style or not.
58 | *
59 | *
60 | * : Tells to generate html with results (default: "report.html").
61 | *
62 | *
63 | * : Tells to generate JUnit XML Log (default: "report.xml").
64 | *
65 | *
66 | * : Tells to generate Tap Log (default: "report.tap.log").
67 | *
68 | *
69 | * : Tells to generate Json Log (default: "report.json").
70 | *
71 | *
72 | * : Tells to use colors in output.
73 | *
74 | *
75 | * : Forces no colors in output (useful to override config file).
76 | *
77 | *
78 | * : Tells to output only suite names and final results.
79 | *
80 | *
81 | * : Determines whether to show test steps in output or not.
82 | *
83 | *
84 | * : Determines whether to show debug and scenario output or not.
85 | *
86 | *
87 | * : Determines whether to run with code coverage (default: "coverage.serialized") or not.
88 | *
89 | *
90 | * : Tells to generate CodeCoverage HTML report in path (default: "coverage").
91 | *
92 | *
93 | * : Tells to generate CodeCoverage XML report in file (default: "coverage.xml").
94 | *
95 | *
96 | * : Tells to generate CodeCoverage text report in file (default: "coverage.txt").
97 | *
98 | *
99 | * : Tells to not finish with exit code.
100 | *
101 | *
102 | * : Environment to use during tests execution.
103 | *
104 | *
105 | * : Tells to stop after first failure.
106 | *
107 | *
108 | * : Tells to not output any message.
109 | *
110 | *
111 | * : Forces ANSI output.
112 | *
113 | *
114 | * : Tells to disable ANSI output.
115 | *
116 | *
117 | * : Tells to not ask any interactive question.
118 | *
119 | * ### EXAMPLE
120 | *
121 | * wp codeception run
122 | * wp codeception run acceptance --steps --html
123 | * wp codeception run acceptance MyAwesomeTest --debug
124 | * wp codeception run functional --env=staging
125 | *
126 | * @synopsis [] [] [--config=] [--report] [--html=] [--xml=] [--tap=] [--json=] [--colors] [--no-colors] [--silent] [--steps] [--debug] [--coverage] [--coverage-html] [--coverage-xml] [--coverage-text] [--no-exit] [--env=] [--fail-fast] [--quiet] [--ansi] [--no-ansi] [--no-interaction]
127 | *
128 | * @since 1.0.0
129 | *
130 | * @todo Implement all arguments for run command (http://codeception.com/docs/reference/Commands).
131 | *
132 | * @access public
133 | * @param array $args Unassociated array of arguments passed to this command.
134 | * @param array $assoc_args Associated array of arguments passed to this command.
135 | */
136 | public function run( $args, $assoc_args ) {
137 | $app = new Application( 'Codeception', \Codeception\Codecept::VERSION );
138 | $app->add( new \WPCC\Command\Run( 'run' ) );
139 | $app->run( new ArgvInput() );
140 | }
141 |
142 | /**
143 | * Executes command.
144 | *
145 | * @since 1.0.0
146 | *
147 | * @access private
148 | */
149 | private function _execute_command() {
150 | $app = new Application( 'Codeception', \Codeception\Codecept::VERSION );
151 | $app->add( new \WPCC\Command\Build( 'build' ) );
152 | $app->add( new \Codeception\Command\Console( 'console' ) );
153 | $app->add( new \WPCC\Command\Bootstrap( 'bootstrap' ) );
154 | $app->add( new \Codeception\Command\GenerateCept( 'generate-cept' ) );
155 | $app->add( new \Codeception\Command\GenerateCest( 'generate-cest' ) );
156 | $app->add( new \Codeception\Command\GenerateTest( 'generate-test' ) );
157 | $app->add( new \Codeception\Command\GeneratePhpUnit( 'generate-phpunit' ) );
158 | $app->add( new \Codeception\Command\GenerateSuite( 'generate-suite' ) );
159 | $app->add( new \Codeception\Command\GenerateHelper( 'generate-helper' ) );
160 | $app->add( new \Codeception\Command\GenerateScenarios( 'generate-scenarios' ) );
161 | $app->add( new \Codeception\Command\Clean( 'clean' ) );
162 | $app->add( new \Codeception\Command\GenerateGroup( 'generate-group' ) );
163 | $app->add( new \Codeception\Command\GeneratePageObject( 'generate-pageobject' ) );
164 | $app->add( new \Codeception\Command\GenerateStepObject( 'generate-stepobject' ) );
165 | $app->run( new ArgvInput() );
166 | }
167 |
168 | /**
169 | * Creates default config, tests directory and sample suites for current
170 | * project. Use this command to start building a test suite.
171 | *
172 | * By default it will create 3 suites acceptance, functional, and unit. To
173 | * customize run this command with --customize option.
174 | *
175 | * ### OPTIONS
176 | *
177 | *
178 | * : Sets manually actors and suite names during setup.
179 | *
180 | *
181 | * : Creates tests with provided namespace for actor classes and helpers.
182 | *
183 | *
184 | * : Sets actor name to create {SUITE}{NAME} actor class.
185 | *
186 | *
187 | * : Sets path to a project, where tests should be placed.
188 | *
189 | * ### EXAMPLE
190 | *
191 | * wp codeception bootstrap
192 | * wp codeception bootstrap --customize
193 | * wp codeception bootstrap --namespace="Frontend\Tests"
194 | * wp codeception bootstrap --actor=Tester
195 | * wp codeception bootstrap path/to/the/project --customize
196 | *
197 | * @synopsis [] [--customize] [--namespace=] [--actor=]
198 | *
199 | * @since 1.0.0
200 | *
201 | * @access public
202 | * @param array $args Unassociated arguments passed to the command.
203 | * @param array $assoc_args Associated arguments passed to the command.
204 | */
205 | public function bootstrap( $args, $assoc_args ) {
206 | $this->_execute_command();
207 | }
208 |
209 | /**
210 | * Creates a new group class.
211 | *
212 | * ### OPTIONS
213 | *
214 | *
215 | * : The group class name to create.
216 | *
217 | *
218 | * : Path to the custom config file.
219 | *
220 | * ### EXAMPLE
221 | *
222 | * wp codeception generate-group Admin
223 | * wp codeception generate-group Admin --config=/path/to/config.yml
224 | *
225 | * @subcommand generate-group
226 | * @synopsis [--config=]
227 | *
228 | * @since 1.0.0
229 | *
230 | * @access public
231 | * @param array $args Unassociated arguments passed to the command.
232 | * @param array $assoc_args Associated arguments passed to the command.
233 | */
234 | public function generate_group( $args, $assoc_args ) {
235 | $this->_execute_command();
236 | }
237 |
238 | /**
239 | * Creates a new test suite.
240 | *
241 | * ### OPTIONS
242 | *
243 | *
244 | * : The suite name to create.
245 | *
246 | *
247 | * : The actor name for the suite.
248 | *
249 | *
250 | * : Path to the custom config file.
251 | *
252 | * ### EXAMPLE
253 | *
254 | * wp codeception generate-suite api
255 | * wp codeception generate-suite integration Code
256 | * wp codeception generate-suite frontend Front
257 | *
258 | * @subcommand generate-suite
259 | * @synopsis [] [--config=]
260 | *
261 | * @since 1.0.0
262 | *
263 | * @access public
264 | * @param array $args Unassociated arguments passed to the command.
265 | * @param array $assoc_args Associated arguments passed to the command.
266 | */
267 | public function generate_suite( $args, $assoc_args ) {
268 | $this->_execute_command();
269 | }
270 |
271 | /**
272 | * Creates a new Cept (scenario-driven test) file.
273 | *
274 | * ### OPTIONS
275 | *
276 | *
277 | * : The suite name where to add a new Cept.
278 | *
279 | *
280 | * : The name for a new Cept file.
281 | *
282 | *
283 | * : Path to the custom config file.
284 | *
285 | * ### EXAMPLE
286 | *
287 | * wp codeception generate-cept suite Login
288 | * wp codeception generate-cept suite Front
289 | * wp codeception generate-cept suite subdir/subdir/testnameCept.php
290 | *
291 | * @subcommand generate-cept
292 | * @synopsis [--config=]
293 | *
294 | * @since 1.0.0
295 | *
296 | * @access public
297 | * @param array $args Unassociated arguments passed to the command.
298 | * @param array $assoc_args Associated arguments passed to the command.
299 | */
300 | public function generate_cept( $args, $assoc_args ) {
301 | $this->_execute_command();
302 | }
303 |
304 | /**
305 | * Creates a new Cest (scenario-driven object-oriented test) file.
306 | *
307 | * ### OPTIONS
308 | *
309 | *
310 | * : The suite name where to add a new Cest.
311 | *
312 | *
313 | * : The name for a new Cest class.
314 | *
315 | *
316 | * : Path to the custom config file.
317 | *
318 | * ### EXAMPLE
319 | *
320 | * wp codeception generate-cest suite Login
321 | * wp codeception generate-cest suite subdir/subdir/testnameCest.php
322 | * wp codeception generate-cest suite "App\Login"
323 | *
324 | * @subcommand generate-cest
325 | * @synopsis [--config=]
326 | *
327 | * @since 1.0.0
328 | *
329 | * @access public
330 | * @param array $args Unassociated arguments passed to the command.
331 | * @param array $assoc_args Associated arguments passed to the command.
332 | */
333 | public function generate_cest( $args, $assoc_args ) {
334 | $this->_execute_command();
335 | }
336 |
337 | /**
338 | * Creates a skeleton for Unit Test that extends \Codeception\TestCase\Test class.
339 | *
340 | * ### OPTIONS
341 | *
342 | *
343 | * : The suite name where to add a new test.
344 | *
345 | *
346 | * : The name for a new test class.
347 | *
348 | *
349 | * : Path to the custom config file.
350 | *
351 | * ### EXAMPLE
352 | *
353 | * wp codeception generate-test unit User
354 | * wp codeception generate-test unit "App\User"
355 | *
356 | * @subcommand generate-test
357 | * @synopsis [--config=]
358 | *
359 | * @since 1.0.0
360 | *
361 | * @access public
362 | * @param array $args Unassociated arguments passed to the command.
363 | * @param array $assoc_args Associated arguments passed to the command.
364 | */
365 | public function generate_test( $args, $assoc_args ) {
366 | $this->_execute_command();
367 | }
368 |
369 | /**
370 | * Creates a skeleton for unit test as in classical PHPUnit.
371 | *
372 | * ### OPTIONS
373 | *
374 | *
375 | * : The suite name where to add a new test.
376 | *
377 | *
378 | * : The name for a new test class.
379 | *
380 | *
381 | * : Path to the custom config file.
382 | *
383 | * ### EXAMPLE
384 | *
385 | * wp codeception generate-phpunit unit User
386 | * wp codeception generate-phpunit unit "App\User"
387 | *
388 | * @subcommand generate-phpunit
389 | * @synopsis [--config=]
390 | *
391 | * @since 1.0.0
392 | *
393 | * @access public
394 | * @param array $args Unassociated arguments passed to the command.
395 | * @param array $assoc_args Associated arguments passed to the command.
396 | */
397 | public function generate_phpunit( $args, $assoc_args ) {
398 | $this->_execute_command();
399 | }
400 |
401 | /**
402 | * Creates an empty Helper class.
403 | *
404 | * ### OPTIONS
405 | *
406 | *
407 | * : The hlper name to create.
408 | *
409 | *
410 | * : Path to the custom config file.
411 | *
412 | * ### EXAMPLE
413 | *
414 | * wp codeception generate-helper MyHelper
415 | *
416 | * @subcommand generate-helper
417 | * @synopsis [--config=]
418 | *
419 | * @since 1.0.0
420 | *
421 | * @access public
422 | * @param array $args Unassociated arguments passed to the command.
423 | * @param array $assoc_args Associated arguments passed to the command.
424 | */
425 | public function generate_helper( $args, $assoc_args ) {
426 | $this->_execute_command();
427 | }
428 |
429 | /**
430 | * Generates user-friendly text scenarios from scenario-driven tests (Cest, Cept).
431 | *
432 | * ### OPTIONS
433 | *
434 | *
435 | * : The suite name to create scenarios for.
436 | *
437 | *
438 | * : Path to the custom config file.
439 | *
440 | *
441 | * : The specified path as destination instead of default.
442 | *
443 | *
444 | * : Specifies output format: html or text (default).
445 | *
446 | *
447 | * : Indicates to render all scenarios to only one file.
448 | *
449 | * ### EXAMPLE
450 | *
451 | * wp codeception generate-scenarios acceptance
452 | * wp codeception generate-scenarios acceptance --format html
453 | * wp codeception generate-scenarios acceptance --path doc
454 | *
455 | * @subcommand generate-scenarios
456 | * @synopsis [--config=] [--path=] [--format=] [--single-file]
457 | *
458 | * @since 1.0.0
459 | *
460 | * @access public
461 | * @param array $args Unassociated arguments passed to the command.
462 | * @param array $assoc_args Associated arguments passed to the command.
463 | */
464 | public function generate_scenarios( $args, $assoc_args ) {
465 | $this->_execute_command();
466 | }
467 |
468 | /**
469 | * Creates a new PageObject class.
470 | *
471 | * ### OPTIONS
472 | *
473 | *
474 | * : The suite name where to add a new page object.
475 | *
476 | *
477 | * : The page object name to create.
478 | *
479 | *
480 | * : Path to the custom config file.
481 | *
482 | * ### EXAMPLE
483 | *
484 | * wp codeception generate-pageobject acceptance Login
485 | *
486 | * @subcommand generate-pageobject
487 | * @synopsis [--config=]
488 | *
489 | * @since 1.0.0
490 | *
491 | * @access public
492 | * @param array $args Unassociated arguments passed to the command.
493 | * @param array $assoc_args Associated arguments passed to the command.
494 | */
495 | public function generate_pageobject( $args, $assoc_args ) {
496 | $this->_execute_command();
497 | }
498 |
499 | /**
500 | * Creates a new StepObject class.
501 | *
502 | * ### OPTIONS
503 | *
504 | *
505 | * : The suite name where to add a new step object.
506 | *
507 | *
508 | * : The step object name to create.
509 | *
510 | *
511 | * : Path to the custom config file.
512 | *
513 | *
514 | * : Determines whether or not to skip verification questions.
515 | *
516 | * ### EXAMPLE
517 | *
518 | * wp codeception generate-stepobject acceptance AdminSteps
519 | *
520 | * @subcommand generate-stepobject
521 | * @synopsis [--config=] [--silent]
522 | *
523 | * @since 1.0.0
524 | *
525 | * @access public
526 | * @param array $args Unassociated arguments passed to the command.
527 | * @param array $assoc_args Associated arguments passed to the command.
528 | */
529 | public function generate_stepobject( $args, $assoc_args ) {
530 | $this->_execute_command();
531 | }
532 |
533 | /**
534 | * Cleans output directory.
535 | *
536 | * ### OPTIONS
537 | *
538 | *
539 | * : Path to the custom config file.
540 | *
541 | * ### EXAMPLE
542 | *
543 | * wp codeception clean --config=/path/to/the/config.yml
544 | *
545 | * @synopsis [--config=]
546 | *
547 | * @since 1.0.0
548 | *
549 | * @access public
550 | * @param array $args Unassociated arguments passed to the command.
551 | * @param array $assoc_args Associated arguments passed to the command.
552 | */
553 | public function clean( $args, $assoc_args ) {
554 | $this->_execute_command();
555 | }
556 |
557 | /**
558 | * Generates Actor classes from suite configs. Currently actor classes are
559 | * auto-generated. Use this command to generate them manually.
560 | *
561 | * ### OPTIONS
562 | *
563 | *
564 | * : Path to the custom config file.
565 | *
566 | * ### EXAMPLE
567 | *
568 | * wp codeception build --config=/path/to/the/config.yml
569 | *
570 | * @synopsis [--config=]
571 | *
572 | * @since 1.0.0
573 | *
574 | * @access public
575 | * @param array $args Unassociated arguments passed to the command.
576 | * @param array $assoc_args Associated arguments passed to the command.
577 | */
578 | public function build( $args, $assoc_args ) {
579 | $this->_execute_command();
580 | }
581 |
582 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------