Enzyme shallow not working in circleci - circleci

I created a test using shallow that work fine locally, but when it's running in circleci in the bitbucket pull request didn't worked. When I changed the test to use mount it work fine locally and in the circleci. Any one have any idea of the reason of this?
Original test:
import React from 'react'
import { shallow } from 'enzyme'
import { Dashboard } from './../Dashboard'
describe('Dashboard', () => {
const wrapper = shallow(<Dashboard />)
it('has a header', () => {
const currentHeader = wrapper.find('Header')
expect(currentHeader.length).toEqual(1)
})
it('has a subHeader', () => {
const currentSubHeader = wrapper.find('SubHeader')
expect(currentSubHeader.length).toEqual(1)
})
it('has a content', () => {
const currentContent = wrapper.find('.sn-layout__content')
expect(currentContent.length).toEqual(1)
})
})
Modified test:
import React from 'react'
import { mount } from 'enzyme'
import { Dashboard } from './../Dashboard'
describe('Dashboard', () => {
const wrapper = mount(<Dashboard />)
it('has a header', () => {
const currentHeader = wrapper.find('.sn-layout__header')
expect(currentHeader.length).toEqual(1)
})
it('has a subHeader', () => {
const currentSubHeader = wrapper.find('.sn-layout__subheader')
expect(currentSubHeader.length).toEqual(1)
})
it('has a content', () => {
const currentContent = wrapper.find('.sn-layout__content')
expect(currentContent.length).toEqual(1)
})
})

Related

Playwright Component Testing with ContextApi

I have created a small React app and I want to test it using Playwright component testing
I have 3 components: App -> ChildComponent -> ChildChildComponent
I want to render (mount) the ChildComponent directly, and make assertions on it, but when I do that, some ContextApi functions that are defined in the App in the normal flow, are now undefined as the App component is not part of the component test.
So i'v trying to render the ChildComponent together with a face ContextApi Provider and pass mocks of those undefined functions, and then I get an infinite render loop for some reason.
How can I go about this, as this use case is typical in react component test.
Here is the test with all my failed mocking attempts separated:
test.only("validate CharacterModal", async ({ page, mount }) => {
const data = ['some-mocked-irrelevant-data']
// const setCurrentCharacter = () => {};
// const setIsCharacterModalOpen = () => {};
// const setCurrentCharacterMocked = sinon.stub("setCurrentCharacter").callsFake(() => {});
// const setIsCharacterModalOpenMocked = sinon.stub("setCurrentCharacter").callsFake(() => {});
// const setCurrentCharacter = jest.fn();
// const setIsCharacterModalOpen = jest.fn();
// const setCurrentCharacter = (): void => {};
// const setIsCharacterModalOpen = (): void => {};
// const setIsCharacterModalOpen = (isCharacterModalOpen: boolean): void => {};
const AppContext = React.createContext<any>(null);
await page.route("**/users*", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(data),
});
});
const component = await mount(
<AppContext.Provider value={{ setCurrentCharacterMocked, setIsCharacterModalOpenMocked }}>
<CharacterModal />
</AppContext.Provider>
);
expect(await component.getByRole("img").count()).toEqual(4);
});
The beforeMount hook can be used for this. I recently added docs about this: https://github.com/microsoft/playwright/pull/20593/files.
// playwright/index.jsx
import { beforeMount, afterMount } from '#playwright/experimental-ct-react/hooks';
// NOTE: It's probably better to use a real context
const AppContext = React.createContext(null);
beforeMount(async ({ App, hooksConfig }) => {
if (hooksConfig?.overrides) {
return (
<AppContext.Provider value={hooksConfig.overrides}>
<App />
</AppContext.Provider>
);
}
});
// src/CharacterModal.test.jsx
import { test, expect } from '#playwright/experimental-ct-react';
import { CharacterModal } from './CharacterModal';
test('configure context through hooks config', async ({ page, mount }) => {
const component = await mount(<CharacterModal />, {
hooksConfig: { overrides: 'this is given to the context' },
});
});

React Styleguidist: How to async set up webpackConfig when using MDXv2 with ES module-based remark/rehype plugins?

In order to use MDX v2 with remark and rehype plugins in react applications created with Create React App v5 I have to use CRACO (v7) and especially its ability to handle a configuration that is returned as a promise/async function. The reason is that CRACO uses CJS module-based configuration, while the remark and rehype plugins are ES modules. Now I want to use MDXv2 with remark/rehype plugins in React Styleguidist too.
This is my craco.config.js:
const { addAfterLoader, loaderByName } = require('#craco/craco')
const mdxplagues = require('./mdxplugins.js')
// https://github.com/facebook/create-react-app/pull/11886#issuecomment-1055054685
const ForkTsCheckerWebpackPlugin =
process.env.TSC_COMPILE_ON_ERROR === 'true'
? require('react-dev-utils/ForkTsCheckerWarningWebpackPlugin')
: require('react-dev-utils/ForkTsCheckerWebpackPlugin');
module.exports = async (env) => {
const mdxplagueConfig = await mdxplagues()
return {
webpack: {
configure: (webpackConfig) => {
addAfterLoader(webpackConfig, loaderByName('babel-loader'), {
test: /\.(md|mdx)$/,
loader: require.resolve('#mdx-js/loader'),
/** #type {import('#mdx-js/loader').Options} */
options: mdxplagueConfig,
})
// https://github.com/facebook/create-react-app/pull/11886#issuecomment-1055054685
webpackConfig.plugins.forEach((plugin) => {
if (plugin instanceof ForkTsCheckerWebpackPlugin) {
plugin.options.issue.exclude.push({ file: '**/src/**/?(*.){spec,test,cy}.*' });
}
})
return webpackConfig
}
}
}
}
And this is my mdxplugins.js that returns an async function returing the needed plugin configurations:
const textrTypoEnDashes = (input) => {
return input
.replace(/ -- /gim, '–')
}
module.exports = async () => {
const remarkGfm = (await import('remark-gfm')).default
const remarkImages = (await import('remark-images')).default
const remarkTextr = (await import('remark-textr')).default
const remarkAccessibleEmoji = (await import('#fec/remark-a11y-emoji')).default
const rehypeSlug = (await import('rehype-slug')).default
const textrTypoApos = (await import('typographic-apostrophes')).default
const textrTypoQuotes = (await import('typographic-quotes')).default
const textrTypoPossPluralsApos = (await import('typographic-apostrophes-for-possessive-plurals')).default
const textrTypoEllipses = (await import('typographic-ellipses')).default
//const textrTypoEmDashes = (await import('typographic-em-dashes')).default
const textrTypoNumberEnDashes = (await import('typographic-en-dashes')).default
return {
remarkPlugins: [
remarkGfm,
remarkImages,
remarkAccessibleEmoji,
[remarkTextr, {
plugins: [
textrTypoApos,
textrTypoQuotes,
textrTypoPossPluralsApos,
textrTypoEllipses,
// textrTypoEmDashes,
textrTypoNumberEnDashes,
textrTypoEnDashes,
],
options: {
locale: 'en-us'
}
}],
],
rehypePlugins: [
rehypeSlug,
],
}
}
My problem is now that I need to apply this webpack/MDXv2 plugin configuration to styleguide.config.js (v13.0.0). I tried...
module.exports = async () => {
return {
...
}
}
...but this doesn't seem to be supported. A look into React Styleguidist's sources, namely scripts/config.ts seems to confirm this.
How can I synchronously resolve my MDX plugin configuration that I return via an async function from my mdxplugin.js CJS module within Styleguidist's CJS configuration module?

How to show loading when uploading many files in uppy library?

im using uppy library in reactjs, when i tried to upload many files nothing happened until the files uploaded (when selecting files in the window dialog and before getting files). How can i catch the event when i choosed the files in the window dialog and before getting them?
Sample code is stated as follows;
import { useState } from 'react'
import Uppy from '#uppy/core'
import thumbnailGenerator from '#uppy/thumbnail-generator'
import { DragDrop } from '#uppy/react'
import { Card, CardHeader, CardTitle, CardBody } from 'reactstrap'
const FileUploaderMulti = () => {
const [previewArr, setPreviewArr] = useState([])
const uppy = new Uppy({
meta: { type: 'avatar' },
autoProceed: true
})
uppy.use(thumbnailGenerator)
uppy.on('thumbnail:generated', (file, preview) => {
const arr = previewArr
arr.push(preview)
setPreviewArr([...arr])
})
const renderPreview = () => {
if (previewArr.length) {
return previewArr.map((src, index) => <img key={index} className='rounded mt-2 mr-1' src={src} alt='avatar' />)
} else {
return null
}
}
return (
<Card>
<CardHeader>
<CardTitle tag='h4'> Multiple Files Upload</CardTitle>
</CardHeader>
<CardBody>
<DragDrop uppy={uppy} />
{renderPreview()}
</CardBody>
</Card>
)
}
export default FileUploaderMulti

NestJS E2E tests with Jest. Injected service returns undefined (only tests)

I have a problem with the end-to-end testing of my users module. I want to validate if there is a "companyCode" when a user makes a GET request in /users and sends this code in the query params. This validator searches the database if this company code exists, if it does not exist it returns an error. The problem is that in the test this validation doesn't happen, because "companiesService" returns undefined (only in the test), what's missing?
Possible Solution: something related to useContainer(class-validator).
Thanks.
users.e2e-spec.ts
describe('UsersController (e2e)', () => {
let app: INestApplication;
let repository: Repository<User>;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [UsersModule, AuthModule, TypeOrmModule.forRoot(ormConfig)],
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
}).compile();
app = module.createNestApplication();
app.useGlobalPipes(new ValidationPipe());
useContainer(app.select(UsersModule), { fallbackOnErrors: true });
repository = module.get('UserRepository');
await app.init();
});
afterAll(async () => {
await app.close();
});
describe('/users (GET)', () => {
it('should return users if requesting user sent "companyCode" in the request body', async (done) => {
return request(app.getHttpServer())
.get('/users')
.auth('admin', 'admin')
.query({ companyCode: '2322661870558778503' }) // should return 200 because companyCode exists but is returning 400
.expect(200)
.then((res) => {
expect(res.body.users).toHaveLength(1);
done();
})
.catch((err) => done(err));
});
});
});
users.module.ts
#Module({
controllers: [UsersController],
providers: [UsersService, UserExistsRule],
imports: [
TypeOrmModule.forFeature([
User,
Person,
Type,
Profile,
UserProfile,
Company,
]),
CompaniesModule,
],
exports: [UsersService],
})
export class UsersModule {}
read-users.dto.ts
export class ReadUsersDto {
#IsOptional()
#IsNotEmpty()
#IsString()
#MinLength(1)
#MaxLength(255)
public name?: string;
#IsOptional()
#IsNotEmpty()
#IsNumberString()
#Type(() => String)
#Validate(CompanyExistsRule)
public companyCode?: string;
}
companies.module.ts
#Module({
providers: [CompaniesService, CompanyExistsRule],
imports: [TypeOrmModule.forFeature([Company, Person])],
exports: [CompaniesService],
})
export class CompaniesModule {}
companies.decorator.ts
#ValidatorConstraint({ name: 'CompanyExists', async: true })
#Injectable()
export class CompanyExistsRule implements ValidatorConstraintInterface {
constructor(private companiesService: CompaniesService) {}
async validate(code: string) {
try {
console.log('companiesService', this.companiesService); // returns undefined on test
await this.companiesService.findOneByCode(code);
} catch (e) {
return false;
}
return true;
}
defaultMessage() {
return `companyCode doesn't exist`;
}
}
I found that I imported useContainer from typeorm instead of the class-validator hahahahha.
// incorrectly imported
import { useContainer } from 'typeorm';
// correctly imported
import { useContainer } from 'class-validator';

Can't open a deeplink in foreground with React Native for iOS

We are using firebase deeplinks in a react native application built for both iOS and Android.
Example deeplink: https://monedacacao.page.link/RifKomEk3bhNM9CW9?d=1
Expected behavior:
User scans a QR Code that contains a deeplink in QRScannerScreen
onSuccess (e) is triggered and the link is opened using Linking.openUr()
In ReduxNavigation Firebase.links().onLink() is triggered and redirects the user to SendDestinataryScreen
Actual behavior
In Android this works as intended, but on iOS Linking.openURL(e.data) opens a browser with the Firebase fallback link instead of triggering the Firebase.links.onLin() action.
If the link is clicked from outside the application it behaves as intended. So this problem only occurs when opening the link from inside the application.
QRScannerScreen.js
...
onSuccess (e) {
Linking
.openURL(e.data)
.catch(err => console.error('An error occured', err))
}
...
ReduxNavigation.js
import React from 'react'
import { BackHandler, Platform } from 'react-native'
import { addNavigationHelpers, NavigationActions } from 'react-navigation'
import { createReduxBoundAddListener } from 'react-navigation-redux-helpers'
import { connect } from 'react-redux'
import firebase from 'react-native-firebase'
import AppNavigation from './AppNavigation'
class ReduxNavigation extends React.Component {
constructor (props) {
super(props)
// handle deeplinking
firebase.links().onLink((url) => {
console.log('URL', url)
if (this.props.token) {
this.props.dispatch(NavigationActions.push({
routeName: 'SendDestinataryScreen',
params: { link: url }
}))
} else {
this.props.dispatch(NavigationActions.push({
routeName: 'LoginScreen'
}))
}
})
}
componentDidMount () {
if (Platform.OS === 'ios') return
BackHandler.addEventListener('hardwareBackPress', () => {
const { dispatch, nav } = this.props
// change to whatever is your first screen, otherwise unpredictable results may occur
if (nav.routes.length === 1 && (nav.routes[0].routeName === 'LaunchScreen')) {
return false
}
// if (shouldCloseApp(nav)) return false
dispatch({ type: 'Navigation/BACK' })
return true
})
}
componentWillUnmount () {
if (Platform.OS === 'ios') return
BackHandler.removeEventListener('hardwareBackPress')
}
render () {
return <AppNavigation navigation={addNavigationHelpers({ dispatch: this.props.dispatch, state: this.props.nav, addListener: createReduxBoundAddListener('root') })} />
}
}
const mapStateToProps = state => ({ nav: state.nav, token: state.authentication.token })
export default connect(mapStateToProps)(ReduxNavigation)

Resources