I can't read all data from my writable store:
Error: 'store' is not exported by src\stores.js, imported by src\components\Users.svelte
The store:
import {subscribe} from 'svelte/internal'
import {writable} from 'svelte/store'
export const fooStore = (key, initial) => {
const foo = localStorage.getItem(key)
const data = foo ? JSON.parse(user) : initial
const store = writable(data, () => {
const unsubscribe = store.subscribe(value => {
localStorage.setItem(key, JSON.stringify(value))
})
return unsubscribe
})
return store
}
The view:
<script>
import {store} from '../stores.js'
</script>
<table>
<thead></thead>
<tbody>
{#each $store as foo}
<tr>
<td>foo.bar</td>
<td>foo.qaz</td>
</tr>
{/each}
</tbody>
</table>
The only thing you are exporting from your store file is a function called fooStore.
When you do import { store } from ... it will look for a named export in that file with the name of store, so it would expect the file to somewhere have export const store = ... or similar.
Either you declare some stores in the store file, if you want you can create them with that fooStore function:
export const fooStore = () => {}
// with fooStore
export const myStore1 = fooStore();
// plain store
export const myStore2 = writable(123);
or you import the function in your component and declare the store there
<script>
import { fooStore } from '...'
const store = fooStore(...)
</script>
Related
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' },
});
});
In my React-Admin app, I'd like to leverage react-hook-form's useFormContext for various things, such as, for example, setting the default pre-selected choice in this custom input field:
...
import {
Create, SimpleForm, SelectInput
} from 'react-admin';
import { useFormContext } from 'react-hook-form';
const MyInput = () => {
const formContext = useFormContext();
formContext.setValue('category', 'tech');
return (
<SelectInput source="category" choices={[
{ id: 'tech', name: 'Tech' },
{ id: 'people', name: 'People' },
]}
/>
);
};
...
const ItemCreate = () => {
return (
<Create>
<SimpleForm>
<MyInput />
</SimpleForm>
</Create>
);
};
...
This sets the pre-selected value of the field, just as intended. But it throws a warning: Cannot update a component ("Form") while rendering a different component ("MyInput")...
Is there some way to achieve this without getting the warning?
Note: The only reason I'm using a custom input field here is because when I put useFormContext() directly into the component that contains SimpleForm it returns null (similarly described here).
The warning is related to the fact that the entire body of the MyInput() function is executed during each render, you need to call the setValue() function inside the useEffect hook.
Got this working by moving formContext.setValue into a useEffect hook:
...
import {
Create, SimpleForm, SelectInput
} from 'react-admin';
import { useFormContext } from 'react-hook-form';
const MyInput = () => {
const formContext = useFormContext();
// moved the setValue into a useEffect
useEffect(() => {
formContext.setValue('category', 'tech');
});
return (
<SelectInput source="category" choices={[
{ id: 'tech', name: 'Tech' },
{ id: 'people', name: 'People' },
]}
/>
);
};
...
const ItemCreate = () => {
return (
<Create>
<SimpleForm>
<MyInput />
</SimpleForm>
</Create>
);
};
...
I'm trying to create a playwright test (in javascript) that uses the page object model of classes, but where the test and page object model aren't in the same directory path.
The problem I'm having is it can't find my page-object-model class file. The error is Error: Cannot find module './pom/home-page'. What am I missing or doing wrong?
My file setup and path structure are as follows:
/package.config.js
...
const config = {
testDir: './test/playwright',
...
/test/playwright/pom/home-page.js
const { expect } = require ('#playwright/test');
exports.HomePage = class HomePage {
constructor(page) {
this.page = page;
this.searchInput = page.locator('#searchInput');
this.searchButton = page.locator('#searchButton');
}
}
/test/playwright/scripts/home/search.spec.js
const {test, expect} = require('#playwright/test');
const {HomePage} = require('./pom/home-page');
test.beforeAll( async ({ page }) => { ... });
test.beforeEach( async ({ page }) => { ... });
test.afterAll( async ({ page }) => { ... });
test.describe( 'As a user I want to search', () => {
test('"mySearchTerm1" and return {the expected result}', async ({ page }) => {
const homePage = new HomePage(page);
...
});
test('"mySearchTerm2" and return {the expected result}', async ({ page }) => {
const homePage = new HomePage(page);
...
});
});
Those using TypeScript can simplify this using tsconfig.json
https://playwright.dev/docs/test-typescript#manually-compile-tests-with-typescript
in tsconfig add:
"baseUrl": ".",
"paths":{
"#pages/*":[
"/test/playwright/pom/*"
]
}
Then you can import it in your fixture or test file like this:
import { HomePage } from "#pages/home-page"
This can be used to shorten fixtures or other files.
So, apparently the file reference is relative to the directory the test is located, not the testDir directory defined in the config file. I need to change line 2 in search.spec.js
const {HomePage} = require('../../pom/home-page');
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
I'm using rails as backend and react native as front end, I'm trying to upload one photo using formdata in react native and using active storage in rails to save it.
using one model name Room.rb and has_one_attached :photo.
Room.rb
class Room < ApplicationRecord
has_one_attached :photo
end
here is the params received by rails, there are two (room_name and photo)
{
"room_name"=>"Guest Room",
"photo"=>
<ActionController::Parameters {
"uri"=>"file:///Users/MyName/Library/Developer/CoreSimulator/Devices/guest_room.jpg",
"name"=>"guest_room.jpg",
"type"=>"image/jpg"
} permitted: true >
}
room_controller.rb to save and receive file as follow
def create
#room = Room.create(room_params)
if #room.save
render json: RoomSerializer.new(#room).serializable_hash, status: :created
else
render json: { errors: #room.errors }, status: :unprocessable_entity
end
end
I get an error inside #room.save, saying 'TypeError - hash key "uri" is not a Symbol:'
my expectation after I choose an image from mobile phone (client) and press save button, it will automatically download an image, this is also the reason I send using FormData from react native.
Update 2:
here is part of react native that upload photo,
const preparePhoto = (uriPhoto) => {
// ImagePicker saves the taken photo to disk and returns a local URI to it
const localUri = uriPhoto;
const name = localUri.split('/').pop();
// Infer the type of the image
const match = /\.(\w+)$/.exec(name);
const type = match ? `image/${match[1]}` : `image`;
return [name, type];
};
const createRoom = dispatch => async ({ room_name, uriPhoto }) => {
const [name, type] = preparePhoto(uriPhoto);
const photo = { uri: uriPhoto, name, type };
const room = { room_name, photo };
const formData = new FormData();
formData.append('room', JSON.stringify(room));
const config = { headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
} };
try {
const response = await serverApi.post('/rooms', formData, config);
dispatch({ type: 'clear_error' });
} catch (err) {
console.log('error: ', err);
dispatch({ type: 'add_error', payload: 'Sorry we have problem' });
}
};
update 3:
source code to choose an image and send it to context
import React, { useState } from 'react';
import Constants from 'expo-constants';
import {
ActivityIndicator,
Button,
Clipboard,
Image,
Share,
StatusBar,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import * as Permissions from 'expo-permissions';
const RoomUploadPhoto = ({ uriPhoto, onPhotoChange }) => {
const [uploading, setUploading] = useState(false);
const renderUploadingIndicator = () => {
if (uploading) {
return <ActivityIndicator animating size="large" />;
}
};
const askPermission = async (type, failureMessage) => {
const { status, permissions } = await Permissions.askAsync(type);
if (status === 'denied') {
alert(failureMessage);
}
};
const handleImagePicked = (pickerResult) => {
onPhotoChange(pickerResult.uri);
};
const takePhoto = async () => {
await askPermission(
Permissions.CAMERA,
'We need the camera permission to take a picture...'
);
await askPermission(
Permissions.CAMERA_ROLL,
'We need the camera-roll permission to read pictures from your phone...'
);
const pickerResult = await ImagePicker.launchCameraAsync({
allowsEditing: true,
aspect: [4, 3],
});
handleImagePicked(pickerResult);
};
const pickImage = async () => {
await askPermission(
Permissions.CAMERA_ROLL,
'We need the camera-roll permission to read pictures from your phone...'
);
const pickerResult = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
aspect: [4, 3],
});
handleImagePicked(pickerResult);
};
const renderControls = () => {
if (!uploading) {
return (
<View>
<View style={styles.viewSatu}>
<Button
onPress={pickImage}
title="Pick an image from camera roll"
/>
</View>
<View style={styles.viewSatu}>
<Button onPress={takePhoto} title="Take a photo" />
</View>
</View>
);
}
};
return (
<React.Fragment>
<Text>upload photo</Text>
{renderUploadingIndicator()}
{renderControls()}
</React.Fragment>
);
};
const styles = StyleSheet.create({
viewSatu: {
marginVertical: 8
}
});
export default RoomUploadPhoto;
Make sure you post File object or base64 content to backend. Your photo is just a json object at the moment contains file path and name.
Please remove the photo param from your room_params.
def room_params
params.require(:room).permit(
:room_name
)
end
And attach your photo when you create the room:
def create
#room = Room.new(room_params)
#room.attach params[:photo]
...