Brand-new Cake project, build.cake written as in Setting Up A New Project, added to a net5.0 console application.
When running dotnet cake, the task Clean is silently skipped by runner.
I ran dotnet cake --target="Clean" --verbosity=normal and received this:
Error: One or more errors occurred. (Could not reach target 'Clean' since it was skipped due to a criteria.)
No idea what criteria is skipping the task.
My build.cake:
var target = Argument("target", "Test");
var configuration = Argument("configuration", "Release");
///////////////////////////////////////////////////////////
// TASKS
//////////////////////////////////////////////////////////
Task("Clean")
.WithCriteria(c => HasArgument("rebuild"))
.Does(() =>
{
CleanDirectory($"./LucroMei/bin/{configuration}");
});
Task("Build")
.IsDependentOn("Clean")
.Does(() =>
{
DotNetCoreBuild("./LucroMei.sln", new DotNetCoreBuildSettings
{
Configuration = configuration,
});
});
Task("Test")
.IsDependentOn("Build")
.Does(() =>
{
DotNetCoreTest("./LucroMei.sln", new DotNetCoreTestSettings
{
Configuration = configuration,
NoBuild = true,
});
});
///////////////////////////////////////////////////////////
// EXECUTION
//////////////////////////////////////////////////////////
RunTarget(target);
The Clean task in your script is defined with criteria, take a look at this line:
.WithCriteria(c => HasArgument("rebuild"))
It means that the task will run only in case the condition is specified. The condition is HasArgument("rebuild"), which is trying to find the argument named rebuild among other arguments specified.
If you run your script like this, the Clean target will run:
dotnet cake --rebuild
The error message is very clear:
Could not reach target 'Clean' since it was skipped due to a criteria
Looking at your Clean target, it has a criteria expectation:
Task("Clean")
.WithCriteria(c => HasArgument("rebuild")) // <<#<<#<<#<<#<<#<<#####
.Does(() => { ... });
This means that your Clean criteria will only run if you provide the argument --rebuild:
dotnet cake --target="Clean" --verbosity=normal --rebuild
Related
I am running a test suite (test1, test2, test3) in Cypress. I noticed when I run this suite for the first time, they all pass. On the next test run, they fail. It seems that it is failing because the data from test3 has not been removed from the cypress browser when test1 begins to run. Could this be a load issue or would adding a logout command at the end of each test case be the solution? Not sure what would be the solution for this.
It seems that it is failing because the data from test3 has not been removed from the cypress browser. First step is to find out where that data being stored ex: cookie, session storage or local storage? Once you found it, you can choose appropriate method to delete data. Incase you are interested in clearing all u can do something as below
beforeEach(() => {
cy.window().then(win => win.sessionStorage.clear());
cy.clearCookies();
cy.clearLocalStorage();
});
Make sure above syntax is correct from cypress.io.
In your support/index.js or support/index.ts file, you could have a call to before(() => {} that loads your browser up once, and then have all of your tests run in one browser session. When you run the tests again, the first thing it will do is reload the page in the browser so that you have a clean session to start from.
Have you tried adding a step to clear the cookies?
Cypress.Commands.add('logout', () => {
cy.clearCookies().then(() => {
cy.getCookies().should('be.empty').visit('/')
})
});
I see 2 solutions:
I. Add an assertion for the logout command - usualy adding an assertion solves this problem.
Cypress.Commands.add("shopFE_logout", (domain) => {
cy.log('shopFE_logout - start')
cy.get('[data-qa="logoutButton"]')
.click({ force: true })
.should('not.exist')
cy.log('shopFE_logout - end')
})
II. The more secure, but not popular way - separate the test cases to be 1 per file and organize them in folders
I found this on cypress docs "Remember, Cypress already automatically clears localStorage, cookies, sessions, etc before each test. Make sure you are not trying to clean up state that is already cleaned up by Cypress automatically."
https://docs.cypress.io/guides/references/best-practices.html#Is-resetting-the-state-necessary.
I don't think adding logout before each test will be a good idea.
In my test, I do login before each test and I have not done logout and still working fine
describe('This is my test suite', () => {
let credential = "";
beforeEach(function () {
cy.visit(Cypress.env('devUrl'));
cy.fixture('data/common/loginData.json').then(loginData => {
credential = loginData;
});
})
it('This is my first testcase', () => {
cy.myLoginCommand(credential.valid.username, credential.valid.password)
objLocation.navToLocationPage()
objLocation.verifySuccessMsg('Successfully Saved')
})
it('This is my second testcase', () => {
cy.myLoginCommand(credential.valid.username, credential.valid.password)
objLocation.navToLocationPage()
objLocation.verifySuccessMsg('Successfully Updated')
})
})
When I press the "run all specs" button or use the run command that runs all files in Cypress it runs all test files alphabetically, so I don't want that.
I want to sort all of them with my own rules.
Let's say I have 3 steps in a chat app test.
Can connect the chat app
Can connect the chat
Can the user send a message
I want to test every step without being tied to each other.
What I mean, Testing one of their own function.
What I do is as follows
chat_app_connect.spec.js
describe('Server Connecting Test', () => {
it('Visit Server page', () => {
cy.visit('https://chat.page..');
});
it('Check welcome messages', () => {
cy.contains('Live Support');
cy.contains('Hello, Stranger');
});
it('Check URL and status of circle', () => {
// URL
cy.url()
.should('include', '/hello');
// Status Circle
cy.get('circle')
.should('have.class', 'positive');
});
});
chat_connect.spec.js
import './chat_app_connect.spec.js';
describe('Chat Connecting Test', () => {
it('Type customer name', () => {
cy.get('input')
.clear()
.type('E2E Test');
});
it('Click to the submit button', () => {
cy.get('.submit-button')
.click();
});
it('Check URL and status of circle', () => {
// URL
cy.url()
.should('equal', 'https://client.dev.octopus.chat/');
// Status Circle
cy.get('circle', { timeout: 5000 })
.should('have.class', 'positive');
});
});
chatting.spec.js
import './chat_connect.spec.js';
describe('Chatting Tests', () => {
it('Type a test message then press Enter and check the message if it sent', () => {
// Type
cy.get('#chat-message')
.clear()
.type('Hey I\'m a test message{enter}');
// Check the message
cy.get('.message-list')
.should('contain', 'Hey I\'m a test message');
});
});
as you see every test is tied to each other, and that is mean when I tried to test just catting functionality its call every test and the whole tests will be tested.
I don't know if it is the right way or not.
what should I do in this case or can it be an acceptable way
I have a particular case where I launch multiple instances of an app, rather than using fixtures or test data, I simply integrate user feedback as Cypress tests from login on forwards.
In any case, I used the specPattern config in cypress.json to set the spec file run order:
{
"baseUrl": "http://localhost:5000",
"specPattern": [
"login/*.js",
"leads/new-lead.spec.js",
"leads/leads-list.spec.js",
"leads/lead-detail.spec.js",
"leads/lead-modify.spec.js",
//...
]
}
No file numbering needed :D
The easiest solution is most likely to add a prefix to all your test files, such as:
01-chat_app_connect.spec.js
02-chat_connect.spec.js
etc.
Cypress is going to take those files in alphabetical order, which you can "trick" into your wanted behavior by using a number as a prefix.
Jean Lescure's answer was a lifesaver. We needed to run tests based on priority without having a bunch of duplicated tests or symlinks. The following worked for us in our default cypress config file:
"integrationFolder":"cypress/integration",
"testFiles": [
"high_priority_specs/**/*.js",
"medium_priority_specs/**/*.js",
"low_priority_specs/**/*.js"
]
To change the level of priority we used 3 configs files that were loaded using the cypress --configFile argument. To run the higher priority tests (smoke tests only) we used the following:
"integrationFolder":"cypress/integration",
"testFiles": [
"high_priority_specs/**/*.js"
]
Cypress does not intentionally let you do this, and for good reasons:
It's generally indicative of poor test design. Tests should not depend on the state of one another. Any test should be able to be run successfully in isolation from the rest of the test suite.
You'll never be able to take advantage of cypress' built in ability to run tests in parallel since you can't guarantee one spec will be ran after another
Here is a relevant discussion about this that gets into more detail: https://github.com/cypress-io/cypress/issues/390
However, if you decide to do this anyway, you can do it by prefixing the name of the specs with a number:
01-some-spec.js
02-alphabetically-first-spec.js
03-some-other-spec.js
In addition to #Brendan answer, if you have a nested folder structure, this approach will work as well.
01-folder-name
|
- 01-some-spec.js
I have a task which restores our NuGet package for our dotnet core application:
Task("Restore-Packages")
.Does(() =>
{
DotNetCoreRestore(sln, new DotNetCoreRestoreSettings {
Sources = new[] {"https://my-team.pkgs.visualstudio.com/_packaging/my-feed/nuget/v3/index.json"},
Verbosity = DotNetCoreVerbosity.Detailed
});
});
However when run on VSTS it errors with the following:
2018-06-14T15:10:53.3857512Z C:\Program Files\dotnet\sdk\2.1.300\NuGet.targets(114,5): error : Unable to load the service index for source https://my-team.pkgs.visualstudio.com/_packaging/my-feed/nuget/v3/index.json. [D:\a\1\s\BitCoinMiner.sln]
2018-06-14T15:10:53.3857956Z C:\Program Files\dotnet\sdk\2.1.300\NuGet.targets(114,5): error : Response status code does not indicate success: 401 (Unauthorized). [D:\a\1\s\BitCoinMiner.sln]
How do I authorize access for the build agent to our private VSTS?
I literally just had this same problem, apparently the build agents in VSTS can't get to your private VSTS feed without an access token so you are going to have to create a Personal Access Token in VSTS and provide that to the built in Cake method to add an authenticated VSTS Nuget feed as one of the sources. Here, I have wrapped it in my own convenience Cake method that checks to see if the package feed is already present, if not, then it adds it:
void SetUpNuget()
{
var feed = new
{
Name = "<feedname>",
Source = "https://<your-vsts-account>.pkgs.visualstudio.com/_packaging/<yournugetfeed>/nuget/v3/index.json"
};
if (!NuGetHasSource(source:feed.Source))
{
var nugetSourceSettings = new NuGetSourcesSettings
{
UserName = "<any-odd-string>",
Password = EnvironmentVariable("NUGET_PAT"),
Verbosity = NuGetVerbosity.Detailed
};
NuGetAddSource(
name:feed.Name,
source:feed.Source,
settings:nugetSourceSettings);
}
}
and then I call it from the "Restore" task:
Task("Restore")
.Does(() => {
SetUpNuget();
DotNetCoreRestore("./<solution-name>.sln");
});
Personally, I prefer to keep PATs away from the source control so here I am reading from env vars. In VSTS you can create an environment variable under the Variables tab of your CI build configuration.
Hope this helps! Here is a link to Cake's documentation.
As pointed out by both #KevinSmith and #NickTurner, a better approach to accessing the VSTS feed is by using the pre-defined system variable System.AccessToken as opposed to using limited validity, manually created and cumbersome PATs. This variable is available on the build agents for the current build to use. More info here.
One way of using this token in the Cake script is as follows:
First, expose the system variable as an environment variable for the Cake task in azure-pipelines.yml
steps:
- task: cake-build.cake.cake-build-task.Cake#0
displayName: 'Cake '
inputs:
target: Pack
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
Then in Cake you can access it like you would any environment variable, so in my case:
if (!NuGetHasSource(source:feed.Source))
{
Information($"Nuget feed {feed.Source} not found, adding...");
var nugetSourceSettings = new NuGetSourcesSettings
{
UserName = "whoosywhatsit",
Password = EnvironmentVariable("SYSTEM_ACCESSTOKEN"),
Verbosity = NuGetVerbosity.Detailed
};
NuGetAddSource(
name:feed.Name,
source:feed.Source,
settings:nugetSourceSettings);
}
This seems to work! If there are better approaches of accessing this variable in Cake please let me know. Please also note in my case, I am only using this to restore packages from my VSTS feed, not for pushing to it. That I do via a DotNetCoreCLI#2 task in the YML like so:
- task: DotNetCoreCLI#2
displayName: 'dotnet nuget push'
inputs:
command: push
packagesToPush: 'artifacts/package.nupkg'
publishVstsFeed: '<id of my VSTS feed>'
And Azure pipeline handles the rest.
I'm playing with the console component of Symfony and I'm facing an issue when it comes to testing.
I have a command that can move some files and display messages depending on the actions performed. I'm using SymfonyStyle to format my output.
I'm using the CommandTester to test my command but if I'm able to test whether the command did something or not, I can't find a efficient way to test its output.
Here is what I'm trying to do :
<?php
public function testIgnoreSamples()
{
$container = $this->application->getContainer();
$container['config'] = [
'source_directory' => vfsStream::url('Episodes/From'),
'target_directory' => vfsStream::url('Episodes/To'),
'ignore_if_nuked' => false,
'delete_nuked' => false,
'search_subtitles' => false,
'prefer_move_over_copy' => false
];
copy(
__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'Fixtures/breakdance.mp4',
vfsStream::url('Episodes/From/sample-angie.tribeca.s01e07.720p.hdtv.x264-killers.mkv')
);
$commandTester = new CommandTester($this->application->find('episodes:move'));
$commandTester->execute([]);
$this->assertContains('because it\'s a sample', $commandTester->getDisplay());
$this->assertEquals([], vfsStream::inspect(new vfsStreamStructureVisitor())->getStructure()['Episodes']['To'], 'Target directory is empty');
}
The issue here is that depending on the console size, my output can be displayed on one or two lines, which makes it hard to write tests that can be executed in different environment.
For example in my environment it's displayed like this :
and in travis like this :
which brakes the tests.
Do you know if the component provides a workaround for this case?
Use the wordwrap function to constrain all output to 75 or so columns.
$output->writeln(wordwrap($long_string));
I finally figured how to fix the terminal size for the tests.
Starting in v3.2, symfony/console allows us to fix the terminal size, which is used by the SymfonyStyle to build the output.
Just call putenv('COLUMNS=80') before executing a command to fix its terminal size.
My test is now :
public function testIgnoreSamples()
{
$container = $this->application->getContainer();
$container['config'] = [
'source_directory' => vfsStream::url('Episodes/From'),
'target_directory' => vfsStream::url('Episodes/To'),
'ignore_if_nuked' => false,
'delete_nuked' => false,
'search_subtitles' => false,
'prefer_move_over_copy' => false
];
copy(
__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'Fixtures/breakdance.mp4',
vfsStream::url('Episodes/From/sample-angie.tribeca.s01e07.720p.hdtv.x264-killers.mkv')
);
putenv('COLUMNS=80');
$commandTester = new CommandTester($this->application->find('episodes:move'));
$commandTester->execute([]);
$expected = <<<'EXPECTED'
! [NOTE] File sample-angie.tribeca.s01e07.720p.hdtv.x264-killers.mkv ignored
! because it's a sample
EXPECTED;
$this->assertContains($expected, $commandTester->getDisplay());
$this->assertEquals([], vfsStream::inspect(new vfsStreamStructureVisitor())->getStructure()['Episodes']['To'], 'Target directory is empty');
}
and the tests are green in travis and in my local environment :).
For development we have a single Symfony console command that executes other console commands in order to rebuild db, run fixtures etc.
As part of the process I need to run a few cherry-picked doctrine migration commands, but for some reason I'm unable to run multiple execute commands within the same process.
To confirm, I can run these tasks without issue manually, and can run one of either command within the console execute and then the other manually without issue.
$this->getApplication()->run(new ArrayInput(array(
'command' => 'doctrine:migrations:execute',
'version' => '20140310162336',
'--no-interaction' => true
)), $output);
$this->getApplication()->run(new ArrayInput(array(
'command' => 'doctrine:migrations:execute',
'version' => '20140310170437',
'--no-interaction' => true
)), $output);
The error returned is:
[Doctrine\DBAL\Migrations\MigrationException]
Migration version 20140310162334 already registered with class Doctrine\DBAL\Migrations\Version
The version being the first version file that exists, can confirm that one is not in the migration_versions table, nor is it wanted in this scenario. Suggesting it is just loaded into the migrations object.
Can anyone offer input if I'm doing something wrong of if this is perhaps a bug somewhere.
Running Symfony 2.2.* and migrations bundle using dev-master.
I had the same problem on symfony 2.6 and the solution described by Alexei Tenitski didn't work althought it seemed a valid one.
This is the solution that worked for me.
/**
* Loop thorugh the config and path config for migrations
* and execute migrations for each connection
*/
foreach (array_keys($this->migrationsConfig) as $configEm) {
if (
(empty($ems) || in_array($configEm, $ems))
&& !in_array($configEm, $ignoreEms)
) {
try {
// new instance of the command you want to run
// to force reload MigrationsConfig
$command = new MigrateSingleCommand($this->migrationsConfig);
$command->setApplication($this->getApplication());
$arguments = [
'command' => $commandString,
'--em' => $configEm,
];
$input = new ArrayInput($arguments);
$command->run($input, $output);
} catch (\Exception $e) {
$output->writeln(sprintf("<error>Error: %s</error>", $e->getMessage()));
}
}
}
if you use $this->getApplication()->run() it will take the command from $this->application->commands where the commands are initialized only once and (when the command calling is initialized) so the MigrationsConfig will stay the same on all iterations.
The problem is that application uses same instance of command for each call and Doctrine migrate commands are not designed to work in such environment. One way to work around it is to clone command and work with its instance directly:
$commandName = 'doctrine:migrations:execute';
$prototypeCommand = $this->getApplication()->get($commandName);
// This is required to avoid merging of application definition for each cloned command
$prototypeCommand->mergeApplicationDefinition();
// Create a clone for a particular run
$command1 = clone $prototypeCommand;
// Run the command with specific params
$command1->run($input1, $output)
// Create another clone
$command2 = clone $prototypeCommand;
// Run the command with another set of params
$command2->run($input2, $output)
My guess is that it is because you are trying to run the migration command multiple times at once.
You might want to try using a work queue system, there is probably even a bundle that does that.