This plugin has been designed as a verification “Swiss army knife” helping journalists to save time and be more efficient in their fact-checking and debunking tasks on social networks especially when verifying videos and images.
The provided tools allow you to quickly get contextual information on Facebook and YouTube videos, perform a reverse image search on Google, Baidu or Yandex search engines, to fragment videos from various platforms (Facebook, Instagram, YouTube, Twitter, Daily Motion) into keyframes, to enhance and explore keyframes and images through a magnifying lens, to query Twitter more efficiently through time intervals and many other filters, to read video and image metadata, and to apply forensic filters on still images.
For further details about the tool and the already released versions, please visit the plugin page on our latest project website.
For any feedback (bugs, enhancement, suggestions), please use the feedback tool within the plugin (on the bottom right).
This plugin was initially developed by the InVID European project (2016-2018), a Horizon 2020 Innovation Action funded by the European Union under Grant Agreement 687786. It has been enhanced by the WeVerify European project (2018-2021), a Horizon 2020 Innovation Action funded by the European Union under Grant Agreement 825297. Since September 2022, it is improved within the vera.ai project under Grant Agreement 101070093.
For more information about those three projects, visit the vera.ai website, the WeVerify website, and InVID website or follow us on veraai_eu, InVID_EU or on WeVerify.
Disclaimer: This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
To setup this project you need to run:
REACT_APP_ELK_URL=<ELK-URL>/twinttweets/_search
REACT_APP_TWINT_WRAPPER_URL=<TWINT-WRAPPER-URL>
REACT_APP_FACEBOOK_APP_ID=<REACT_ID>
REACT_APP_TRANSLATION_GITHUB=https://raw.githubusercontent.com/AFP-Medialab/InVID-Translations/react/
REACT_APP_KEYFRAME_TOKEN=<yourKeyframeToken>
REACT_APP_MY_WEB_HOOK_URL=<yourSlackAppUrlHook>
REACT_APP_GOOGLE_ANALYTICS_KEY=<yourGoogleAnaliticsToken>
REACT_APP_MAP_TOKEN=<MAP_TOKEN>
REACT_APP_BASEURL=<TWINT-WRAPPER-URL>
####Then you need to load the extension in your browser:
Build the extension using the available scripts to generate the file dev
or build
. (#some-markdown-heading)
- In chrome menu go to
More tools
then clickExtentions
- Activate the
Developer mode
toggle - The click the
Load Unpacked
button - Select the
dev
orbuild
file you generated earlier.
- In firefox menu click on
Add-ons
- Then click on the gear button
⚙⌄
- Then click on
Debug Add-ons
- Then click on
Load TEmporary Add-on...
- Select the
manifest.json
in thedev
orbuild
file you generated earlier.
In the project directory, you can run:
Runs the app in the development mode.
This will run on port 3000.
The extension will reload if you make edits.
You will also see any lint errors in the console.
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
##Project structure
public
: Contains the basic files for an extension (manifest.json etc) and the root htlm file (popup.html)
src/background
: Contains the srcipts that are executed in the web browser background by the extension. The right click menu for example.
src/contentScript
: Contains the srcipts executed in the visited pages.(Now empty)
src/Hooks
: Contains react custom hooks used in this project.
src/LocalDictionary
: Contains the components
file from this github repository (This is used as a local backup for the extension if the github is unavailable)
src/redux
: Contains react-redux actions and reducers. Documentation basic Tutorial
####src/components
This file contains all the different components that are used in this project.
src/components/Shared
: Contains all the components that are reused in multiple different components.
src/components/FeedBack
: The FeedBack tool
src/components/PopUp
: The PopUp of the extension.
src/components/MySnackBar
: The little arrow that enable you to get back to the top of the page quickly.
src/components/Navbar
: The NavBar and its Drawer (for tools)
src/components/NavItems
: All components that the navBar can display.
src/components/NavItems/tools
: All components that the tools drawer
can display.
- First go to
src/components/Navbar/Navbar.js
file. Add an object to thetabItems
list like so :
import yourImage from "./path/to/your/image";
const tabItems = [
{
title: "navbar_tools",
icon:
<Icon classes={{root: classes.iconRootTab}} fontSize={"large"}>
<img className={classes.imageIconTab} src={toolIcon}/>
</Icon>,
content: <div/>,
path: "tools",
footer: <div/>,
},
//(...)
{
title: "navbar_factCheck",
icon: <ImageSearchIcon fontSize={"large"}/>,
content: <FactCheck/>,
path: "factCheck",
footer: <Footer type={"afp"}/>
},
{
title: "navbar_example", // Your tsv keyword for the title that you have to add to NavBar.tsv and allTools.tsv>
icon: <Icon classes={{root: classes.iconRootTab}} fontSize={"large"}>
<img className={classes.imageIconTab} src={yourImage}/> // give our custom image
</Icon>,
content: <div/>, // the component you want to add
path: "example", // This name will appear im the url when your tab is selected.
footer: <div/> // The appropriate footer (See `src/components/Shared/Footer` component)
}
];
###⚠
Dont forget to add title
to NavBar.tsv
file.
path
must be unique. No other object in the list should have the same path
value.
###⚠
- First go to
src/components/Navbar/Navbar.js
file. Add an object to thedrawerItems
list like so :
const drawerItems = [
{
title: "navbar_tools",
icon: <AppsIcon fontSize={"large"}
className={(drawerValue === 0) ? classes.selectedApp : classes.unSelectedApp}/>,
tsvPrefix: "all",
path: "all",
},
//(...)
{
title: "navbar_twitter_sna",
icon: (drawerValue === 9) ? twitterSnaIconOn : twitterSnaIconOff,
tsvPrefix: "twitter_sna",
path: "twitterSna"
},
{
title: "navbar_example", // Your tsv keyword for the title that you have to add to NavBar.tsv and allTools.tsv>
icon: (drawerValue === 10) ? IconOn : IconOff, //An image or icon that changes on 'drawerValue' (when === 10 here)
tsvPrefix: "example_sna", // tsvPrefix + "_help_video" should be found in allTools.tsv
path: "example" // This name will appear im the url when your tools is selected.
}
];
###⚠
Dont forget to add title
to NavBar.tsv
and allTools.tsv
files.
Dont forget to add tsvPrefix + "_help_video"
to allTools.tsv
file.
path
must be unique. No other object in the list should have the same path
value.
###⚠
- go to
src/components/NavBar/DrawerItem/DrawerItem.js
file. Add an object to thedrawerItemsContent
list like so :
const drawerItemsContent = [
{
content: <AllTools tools={props.drawerItems}/>,
footer: <div/>
},
//(...)
{
content: <div/>,
footer: <Footer type={"afp"}/>
},
{
content: <YourNewToolComponent/>, // Your new tool component
footer: <div/> // The appropriate footer (See `src/components/Shared/Footer` component)
},
];
At this point you can display the component you want when it's selected in the drawer or the all tools menu. Your component will be unmounted and remounted each time you switch to another item. This means the results/status of your component will be lost. To counter this, the results you computed should be stored with redux.
##Redux src/redux
Redux enables you to manage a global state accessible from anny component. These are the steps to add redux to your component.
- Create a reducer
go to src/redux/reducers
and create a file in the appropriate location calling it <YourTool>Reducer.js
- create an exported function called
<YourTool>Reducer
in this file This function takes two parameters:defaultState
andaction
defaultState
: is the default state you will have in your redux global state.
action
: is the object that contains the action to do action.type
and he action parameters action.payload
(we will go into details later)
This function should contain a switch for action.type
.
This function returns the state value.
const defaultState = true;
const exampleReducer = (state = defaultState, action) => {
switch (action.type) {
case "SET_TRUE":
return true;
case "SET_FALSE":
return false;
case "SET_CUSTOM":
return action.payload;
case "TOGGLE_STATE":
return !state;
default:
return state;
}
};
export default exampleReducer;
In this example if action.type
equals :
SET_TRUE
the new state will be true.SET_FALSE
the new state will be false.SET_CUSTOM
the new state will be 'action.payload' value.TOGGLE_STATE
the new state will be set to the opposite.
By default the state doesnt change.
reducers can be more complex and use objects as default state. have a look to src/redux/reducers/tools/analysisReducer.js
- Add your new reducer to the
src/redux/reducers/index.js
file.
import it and add it to the allReducers
variable.
// imports...
import exampleReducer from "./your/path";
const allReducers = combineReducers({
language : languageReducer,
//(...)
videoRights : videoRightsReducer,
example : exampleReducer,
});
export default allReducers;
- Create your actions
Go to src/redux/actions
and create a file in the appropriate location calling it <YourTool>Actions.js
- Create functions corresponding to the action.type you needed.
For example, I used SET_TRUE
, SET_FALSE
,SET_CUSTOM
,TOGGLE_STATE
.
I will create :
export const setTrue = () => {
return {
type : "SET_TRUE",
}
};
export const setFalse = () => {
return {
type : "SET_FALSE",
}
};
//setCustom has a parameter passed as payload
export const setCustom = (bool) => {
return {
type : "SET_CUSTOM",
payload : bool
}
};
export const toggleState = () => {
return {
type : "TOGGLE_STATE",
}
};
- You can then use these actions in anny component like this :
import React from "react";
import {useDispatch, useSelector} from "react-redux";
import {setTrue, setFale, setCustom, toggleState} from "./path/to/exampleActions.js";
const Example = () => {
const myReduxState = useSelector(state => state.example);
const dispatch = useDispatch();
const setReduxStateToTrue = () => dispatch(setTrue());
const setReduxStateCustom = (bool) => dispatch(setCustom(bool));
return (
<div>
The redux state is set to {myReduxState}.
<button onClick={setReduxStateToTrue}>
action setTrue example
</button>
<button onClick={() => setReduxStateCustom(true)}>
action setCustom example (with param)
</button>
</div>
);
}
The playwright framework is used for e2e, component and unit testing. All tests are located in the tests
folder with the following structure:
tests
!- component_unit - Component and unit testing
|- e2e - End to end testing
-- examples - Examples to serve as a reference only
Note: Before running all (or just e2e) tests, the extension must first be built. See End-to-end (e2e) testing section for more details
All tests (e2e, component & unit) can be run using the command:
npm run test
Component and unit testing are run together using the following command:
npm run test-cu
Playwright configurations of the component and unit tests can be found in /playwright-ct.config.js
Component and unit testing uses the experimental playwright component testing module.
When running tests, the browser loads the page in ./playwright/index.html
and any resources in ./playwright/index.jsx
.
Tests can then mount a component directly on the page and run tests similar to e2e testing.
Transpiling and bundling of assets are done on the fly using vitejs instead
of webpack.
Note: Component testing is best done on components that can be easily isolated. Ones that rely on redux or routing are likely to be more suitable for e2e testing.
Note: Playwright/vite generates cache files at /playwright/.cache
which sometimes does not get updated properly
when .jsx
files changes, so currently the command npm run test-cu
deletes the cache directory before running the tests.
E2E testing can be run using the following command:
npm run test-e2e
Playwright configurations of the e2e tests can be found in /playwright.config.ts
.
Before running all (or just e2e) tests, the extension must first be built using npm run build
.
The compiled Weverify extension in the ./build
directory can be loaded into the browser by using the fixture code in
/tests/e2e/fixtures.ts
. All e2e tests of the extension should import the overridden test
and expect
functions in
the fixtures.ts
file.
The overridden test
function provides an additional extensionId
variable in the handler function which is
needed to navigate to the location of the extension.
The following code shows how we can navigate to the root page of the extension:
import { test, expect } from './fixtures';
test('Example extension test', async ({ page, extensionId }) => {
// Navigates to the root page of the plugin
await page.goto(`chrome-extension://${extensionId}/popup.html`);
// ... test whatever functions
});
UI mode is for e2e test can be run using:
npm run test-e2e-ui
This will open a browser with interactive testing environment which can be useful for debugging.