Lighthouse auto model detection doesn't work with unit tests - phpunit

I seem to be facing the general problem that directive driven mutations won't properly work when unit testing them. Using GraphQL Playground everything works as expected and the User model is auto detected. But if unit testing the same functionality it fails.
All config values are properly set (as I can see from regular requests not failing):
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => ClientName\PackageName\Models\User::class,
],
...
]
And lighthouse config values also set to the namespace above!
So for example this mutation would trigger the UserPolicy class and after that automatically call the create method of the User class:
type Mutation #protect(guards: ["api"]) {
"Create user"
createUser(data: NewUserInput! #spread): User
#can(ability: "create")
#create
}
But calling the same request via phpunit would result in errors:
for #can it returns a not authorized error and if I remove #can it only updates the email field of the user model as if mass assignment is not properly set.
When I now change the above schema definition to:
type Mutation #protect(guards: ["api"]) {
"Create user"
createUser(data: NewUserInput! #spread): User
#can(ability: "create" model: "ClientName\\PackageName\\Models\\User")
#create(model: "ClientName\\PackageName\\Models\\User")
}
And it works as expected within phpunit tests!
Now I suspect that within tests there is not User model found or not the appropriate but I cannot understand why this is.
Does anyone have any idea?

Related

phpunit testable livewire fails with Undefined array key "fingerprint"

When trying a test that came with Laravel and Jetstream/Livewire libraries, I get an undefined array key "fingerprint" error message
Undefined array key "fingerprint"
at vendor/livewire/livewire/src/Testing/TestableLivewire.php:181
public function pretendWereSendingAComponentUpdateRequest($message, $payload)
{
$result = $this->callEndpoint('POST', '/livewire/message/'.$this->componentName, [
'fingerprint' => $this->payload['fingerprint'],
'serverMemo' => $this->payload['serverMemo'],
'updates' => [['type' => $message, 'payload' => $payload]],
]);
This happens for any out of the box feature tests that ship with Laravel9 with Jetstream when used against my project.
Here is one example that fails at the Livewire::test.... line.
The user is created and authenticating without issue and confirmed in other phpunit tests.
class BrowserSessionsTest extends TestCase
{
use RefreshDatabase;
public function test_other_browser_sessions_can_be_logged_out(): void
{
$this->actingAs($user = User::factory()->create());
Livewire::test(LogoutOtherBrowserSessionsForm::class)
->set('password', $user->password)
->call('logoutOtherBrowserSessions')
->assertSuccessful();
}
}
I stood up a fresh Laravel 9 project which works and began inserting various areas from my project into the fresh project as a way of hopefully identifying the issue. Session parameters, events, migrations, factories, models, were not the issue as it continued to work in the fresh project.
One thing I noticed is that the generic routes are not accepted in my project within he test cases. I have to insert 'https://realtor.host' in front of every test route (e.g. $response = $this->get('https://realtor.host/register');
I was curious if it was not evaluating the livewire route and I tried to add my domain into the vendor's livewire component in which the test still failed and that did not cause it to work.
Any ideas on where else I can look?

Show test body, test steps for passing tests in Cypress

When tests pass in Cypress it doesn't show the steps but if fails it shows all the steps and what step caused test to fail.
I want to see passing tests body/steps too.
Below image shows a passing test and a failing test. Failing test is much more informative. I want passing test to be like that too. How can I achieve this?
it('should select shipment method and type "test" into additional
notes', function () {
cy.intercept('GET',
'**/GetDetailWithAvailableCampaign*').as('basketDetails')
cy.on('uncaught:exception', (err, runnable) => { // If CheckoutJS throws an error, it will be caught here
cy.get('[data-cy="information::forward"]').click()
cy.url().should('include', '/basket/checkout/payment')
cy.get('[data-cy="shipping::shipment_type"]').first().click()
cy.get('[data-cy="shipping::shipment_type"]').should('be.checked')
cy.get('[data-cy="misc::additional_message_checkbox"]').check({force: true}).should('be.checked')
cy.get('[data-cy="misc::additional_message_textarea"]').should('be.visible').clear().type('Test')
// there is no shipping::forward
// cy.get('[data-cy="shipping::forward"]').click()
cy.get('[data-cy="information::forward"]').click()
})
})
it('should focus iframe and put a credit card', function () {
cy.wait(4000)
cy.url().should('include', '/basket/checkout/shipping')
const iframeSelector = 'iframe[data-cy="payment::iframe"]'
getIframeBody(iframeSelector).find('.btn-card-visa').click()
cy.wait(2000)
getIframeBody(iframeSelector).find('input#CardNumber').clear().type(Cypress.env('credit_card').number)
getIframeBody(iframeSelector).find('input#Expiry').clear().type(Cypress.env('credit_card').expiry)
getIframeBody(iframeSelector).find('input#HolderName').clear().type(Cypress.env('credit_card').holder_name)
getIframeBody(iframeSelector).find('input#VerificationCode').clear().type(Cypress.env('credit_card').cvv)
getIframeBody(iframeSelector).find('.btn-next').click()
}
By default, clicking on a test's name in the runner will expand and show all steps executed. In your case, it is doing this. But the issue you are running into is most likely that the uncaught:exception event you are waiting for in the first test is not occurring, and therefore no steps are executed. If you remove the cy.on('uncaught:exception'), you'll see the steps are executed.

Cypress - How can I run test files in order

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

Simple GetItem with ctx.identity.username returns null

I'm using AppSync with IAM auth with a DynamoDB resolver and Cognito. I'm trying to do the following.
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"userId": $util.dynamodb.toDynamoDBJson($ctx.identity.username)
}
}
$ctx.identity.username is supposed to contain userId generated by Cognito and I'm trying to use it to fetch current user data.
Client side, I'm using AWS Amplify that tells me I'm currently logged:
this.amplifyService.authStateChange$.subscribe(authState => {
if (authState.state === 'signedIn') {
this.getUserLogged().toPromise();
this._isAuthenticated.next(true);
}
});
getUserLogged is the Apollo query that is supposed to returns user data.
What I've tried:
If I leave it like this, getUserLogged returns null.
If I replace in the resolver $util.dynamodb.toDynamoDBJson($ctx.identity.username) with a known userId like this $util.dynamodb.toDynamoDBJson("b1ad0902-2b70-4abd-9acf-e85b62d06fa8"): It works! I get this user data.
I tried to use the test tool in the resolver page but it only gives fake data so I can't rely on this.
Did I make a mistake? To me everything looks good but I guess I'm missing something?
Can I clearly see what $ctx.identity contains?
You'll want to use $ctx.identity.cognitoIdentityId to identify Cognito IAM users:
https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html#aws-appsync-resolver-context-reference-identity
You could see the contents of $ctx.identity by creating a Lambda resolver and logging the event or by creating a local resolver and returning the input that the mapping template receives:
https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-local-resolvers.html
My cognitoIdentityId looks like this: eu-west-1:27ca1e79-a238-4085-9099-9f1570cd5fcf

Extjs 4 - Retrieve data in json format and load a Store. It sends OPTION request

I'm developing an app with Spring MVC and the view in extjs 4. At this point, i have to create a Grid which shows a list of users.
In my Spring MVC controller i have a Get method which returns the list of users in a jsonformat with "items" as a root.
#RequestMapping(method=RequestMethod.GET, value="/getUsers")
public #ResponseBody Users getUsersInJSON(){
Users users = new Users();
users.setItems(userService.getUsers());
return users;
}
If i try to access it with the browser i can see the jsondata correctly.
{"items":[{"username":"name1",".....
But my problem is relative to request of the Ext.data.Store
My Script is the following:
Ext.onReady(function(){
Ext.define('UsersList', {
extend: 'Ext.data.Model',
fields: [
{name:'username', type:'string'},
{name:'firstname', type:'string'}
]
});
var store = Ext.create('Ext.data.Store', {
storeId: 'users',
model: 'UsersList',
autoLoad: 'true',
proxy: {
type: 'ajax',
url : 'http://localhost:8080/MyApp/getUsers.html',
reader: {type: 'json', root: 'items'}
}
});
Ext.create('Ext.grid.Panel',{
store :store,
id : 'user',
title: 'Users',
columns : [
{header : 'Username', dataIndex : 'username'},
{header : 'Firstname', dataIndex: 'firstname'}
],
height :300,
width: 400,
renderTo:'center'
});
});
When the store tries to retrieve the data and launchs the http request, in my firebug console appears OPTIONS getUsers.html while the request in the browser launchs GET getUsers.html
As a result, Ext.data.Store has not elements and the grid appears with the columnames but without data. Maybe i've missed something
Thank you
You can change the HTTP methods that are used by the proxy for the different CRUD operations using actionMethods.
But, as you can see in the doc (and as should obviously be the case), GET is the default for read operations. So the OPTIONS request you are observing is quite puzzling. Are you sure that there's not another part of your code that overrides the default application-wide? Maybe do a search for 'OPTIONS' in all your project's JS files, to try and find a possible suspect. Apparently there's no match in the whole Ext code, so that probably doesn't come from the framework.
Edit:
Ok, I think I've got it. If your page is not accessed from the same domain (i.e. localhost:8080, the port is taken into account), the XHR object seems to resort to an OPTIONS request.
So, to fix your problem, either omit the domain name completely, using:
url: '/MyApp/getUsers.html'
Or double check that your using the same domain and port to access the page and make the requests.

Resources