Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@edx/studio-frontend

Package Overview
Dependencies
Maintainers
13
Versions
116
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@edx/studio-frontend - npm Package Compare versions

Comparing version 0.7.1 to 0.8.0

src/data/actions/pingStudio.test.jsx

4

package.json
{
"name": "@edx/studio-frontend",
"version": "0.7.1",
"version": "0.8.0",
"description": "The frontend for the Open edX platform",

@@ -16,3 +16,3 @@ "repository": "edx/studio-frontend",

"@edx/edx-bootstrap": "^0.4.3",
"@edx/paragon": "^1.6.0",
"@edx/paragon": "^1.7.1",
"babel-polyfill": "^6.26.0",

@@ -19,0 +19,0 @@ "classnames": "^2.2.5",

@@ -24,8 +24,21 @@ import React from 'react';

it('correct number of filters', () => {
expect(wrapper.find('li')).toHaveLength(5);
const checkBoxGroup = wrapper.find('CheckBoxGroup');
expect(checkBoxGroup).toHaveLength(1);
expect(checkBoxGroup.find('[type="checkbox"]')).toHaveLength(5);
});
it('correct styling', () => {
expect(wrapper.find('ul').hasClass('filter-set')).toEqual(true);
expect(wrapper.find('h4')).toHaveLength(1);
expect(wrapper.find('h4').hasClass('filter-heading')).toEqual(true);
expect(wrapper.find('div').at(1).hasClass('filter-set')).toEqual(true);
});
it('handles onChange callback correctly', () => {
const checkBoxGroup = wrapper.find('CheckBoxGroup');
let checkBoxes = checkBoxGroup.find('[type="checkbox"]');
checkBoxes.first().simulate('change', { target: { checked: true, type: 'checkbox' } });
checkBoxes = checkBoxGroup.find('[type="checkbox"]');
expect(checkBoxes.first().html()).toContain('checked');
});
});
});
import React from 'react';
import PropTypes from 'prop-types';
import CheckBoxGroup from '@edx/paragon/src/CheckBoxGroup';
import CheckBox from '@edx/paragon/src/CheckBox';

@@ -33,14 +34,19 @@ import { connect } from 'react-redux';

export const AssetsFilters = ({ assetsFilters, updateFilter }) => (
<ul className={styles['filter-set']}>
{ASSET_TYPES.map(type => (
<li key={type.key}>
<CheckBox
name={type.key}
label={type.displayName}
checked={assetsFilters[type.key]}
onChange={checked => updateFilter(type.key, checked)}
/>
</li>
))}
</ul>
<div role="group" aria-labelledby="filter-label">
<h4 id="filter-label" className={styles['filter-heading']}>Filter by File Type</h4>
<div className={styles['filter-set']}>
<CheckBoxGroup>
{ASSET_TYPES.map(type => (
<CheckBox
key={type.key}
id={type.key}
name={type.key}
label={type.displayName}
checked={assetsFilters[type.key]}
onChange={checked => updateFilter(type.key, checked)}
/>
))}
</CheckBoxGroup>
</div>
</div>
);

@@ -47,0 +53,0 @@

@@ -188,3 +188,3 @@ import React from 'react';

} else {
expect(row.containsMatchingElement(<td>Preview not available</td>)).toEqual(true);
expect(row.find('.no-image-preview')).toHaveLength(1);
}

@@ -525,4 +525,4 @@ });

describe('Lock asset', () => {
const getLockedButtons = () => wrapper.find('button > .fa-lock');
const getUnlockedButtons = () => wrapper.find('button > .fa-unlock');
const getLockedButtons = () => wrapper.find('button .fa-lock');
const getUnlockedButtons = () => wrapper.find('button .fa-unlock');
const getLockingButtons = () => wrapper.find('button > .fa-spinner');

@@ -529,0 +529,0 @@ beforeEach(() => {

@@ -12,2 +12,3 @@ import React from 'react';

import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
import styles from './AssetsTable.scss';
import { assetActions } from '../../data/constants/actionTypes';

@@ -121,7 +122,10 @@ import { assetLoading } from '../../data/constants/loadingTypes';

const baseUrl = this.props.courseDetails.base_url || '';
return thumbnail ? (<img src={`${baseUrl}${thumbnail}`} alt="Description not available" />) : 'Preview not available';
if (thumbnail) {
return (<img src={`${baseUrl}${thumbnail}`} alt="Description not available" />);
}
return (<div className={styles['no-image-preview']}>Preview not available</div>);
}
getLockButton(asset) {
const classes = [FontAwesomeStyles.fa];
const classes = [FontAwesomeStyles.fa, styles['button-primary-outline']];
let lockState;

@@ -136,3 +140,4 @@ if (asset.locked) {

return (<Button
label={(<span className={classNames(...classes)} />)}
className={classes}
label={''}
data-asset-id={asset.id}

@@ -165,5 +170,7 @@ aria-label={`${lockState} ${asset.display_name}`}

getLoadingLockButton(asset) {
const classes = [FontAwesomeStyles.fa, FontAwesomeStyles['fa-spinner'], FontAwesomeStyles['fa-spin']];
// spinner classes are applied to the span to keep the whole button from spinning
const spinnerClasses = [FontAwesomeStyles.fa, FontAwesomeStyles['fa-spinner'], FontAwesomeStyles['fa-spin']];
return (<Button
label={(<span className={classNames(...classes)} />)}
className={[styles['button-primary-outline']]}
label={(<span className={classNames(...spinnerClasses)} />)}
aria-label={`Updating lock status for ${asset.display_name}`}

@@ -238,3 +245,3 @@ />);

<span>
{studioUrl && this.getCopyUrlButton(assetDisplayName, studioUrl, 'Studio')}
{studioUrl && this.getCopyUrlButton(assetDisplayName, studioUrl, 'Studio', [styles['studio-copy-button']])}
{webUrl && this.getCopyUrlButton(assetDisplayName, webUrl, 'Web')}

@@ -245,6 +252,6 @@ </span>

getCopyUrlButton(assetDisplayName, url, label) {
getCopyUrlButton(assetDisplayName, url, label, classes = []) {
const buttonLabel = (
<span>
<span className={classNames(FontAwesomeStyles.fa, FontAwesomeStyles['fa-files-o'])} aria-hidden />
<span className={classNames(FontAwesomeStyles.fa, FontAwesomeStyles['fa-files-o'], styles['copy-icon'])} aria-hidden />
{label}

@@ -258,2 +265,3 @@ </span>

label={buttonLabel}
className={classes}
textToCopy={url}

@@ -278,3 +286,3 @@ onCopyButtonClick={this.onCopyButtonClick}

key={currentAsset.id}
className={[FontAwesomeStyles.fa, FontAwesomeStyles['fa-trash']]}
className={[FontAwesomeStyles.fa, FontAwesomeStyles['fa-trash'], styles['button-primary-outline']]}
label={''}

@@ -281,0 +289,0 @@ aria-label={`Delete ${currentAsset.display_name}`}

@@ -64,3 +64,3 @@ import React from 'react';

<Button
className={[styles['copy-button']]}
className={[...this.props.className, styles['copy-button']]}
aria-label={this.props.ariaLabel}

@@ -84,2 +84,3 @@ label={label}

onCopyButtonClick: PropTypes.func,
className: PropTypes.arrayOf(PropTypes.string),
};

@@ -90,2 +91,3 @@

ariaLabel: 'Copy',
className: [],
};

@@ -6,3 +6,3 @@ import configureStore from 'redux-mock-store';

import endpoints from '../api/endpoints';
import { submitAccessibilityForm, clearAccessibilityStatus } from './accessibility';
import * as actionCreators from './accessibility';
import { accessibilityActions } from '../../data/constants/actionTypes';

@@ -20,3 +20,7 @@

describe('Accessibility Action Creator', () => {
describe('Accessibility Action Creators', () => {
beforeEach(() => {
store = mockStore(initialState);
});
afterEach(() => {

@@ -26,3 +30,30 @@ fetchMock.reset();

});
it('returns expected state from submitAccessibilityFormSuccess', () => {
const response = {
status: 200,
};
const expectedAction = {
statusCode: response.status,
type: accessibilityActions.ACCESSIBILITY_FORM_SUBMIT_SUCCESS,
};
expect(store.dispatch(
actionCreators.submitAccessibilityFormSuccess(response))).toEqual(expectedAction);
});
it('returns expected state from submitAccessibilityFormRateLimitFailure', () => {
const response = {
status: 429,
detail: 'You have hit the rate limit!',
};
const expectedAction = {
statusCode: 429,
failureDetails: response.detail,
type: accessibilityActions.ACCESSIBILITY_FORM_SUBMIT_RATE_LIMIT_FAILURE,
};
expect(store.dispatch(
actionCreators.submitAccessibilityFormRateLimitFailure(response))).toEqual(expectedAction);
});
it('returns expected state from success', () => {

@@ -33,4 +64,4 @@ fetchMock.once(zendeskEndpoint, 201);

];
store = mockStore(initialState);
return store.dispatch(submitAccessibilityForm()).then(() => {
return store.dispatch(actionCreators.submitAccessibilityForm()).then(() => {
// return of async actions

@@ -51,4 +82,4 @@ expect(store.getActions()).toEqual(expectedActions);

}];
store = mockStore(initialState);
return store.dispatch(submitAccessibilityForm()).then(() => {
return store.dispatch(actionCreators.submitAccessibilityForm()).then(() => {
// return of async actions

@@ -61,5 +92,4 @@ expect(store.getActions()).toEqual(expectedActions);

const expectedAction = { type: accessibilityActions.CLEAR_ACCESSIBILITY_STATUS };
store = mockStore(initialState);
expect(store.dispatch(clearAccessibilityStatus())).toEqual(expectedAction);
expect(store.dispatch(actionCreators.clearAccessibilityStatus())).toEqual(expectedAction);
});
});

@@ -18,5 +18,5 @@ import * as clientApi from '../api/client';

export const assetDeleteFailure = response => ({
type: assetActions.DELETE_ASSET_FAILURE,
response,
export const requestAssetsFailure = response => ({
type: assetActions.REQUEST_ASSETS_FAILURE,
data: response,
});

@@ -45,3 +45,3 @@

.catch((error) => {
dispatch(assetDeleteFailure(error));
dispatch(requestAssetsFailure(error));
});

@@ -64,16 +64,23 @@

export const deleteAssetSuccess = (assetId, response) => ({
export const deleteAssetSuccess = assetId => ({
type: assetActions.DELETE_ASSET_SUCCESS,
assetId,
response,
});
export const deleteAssetFailure = assetId => ({
type: assetActions.DELETE_ASSET_FAILURE,
assetId,
});
export const deleteAsset = (assetId, courseDetails) =>
dispatch =>
clientApi.requestDeleteAsset(courseDetails.id, assetId)
// since the API returns 204 on success and 404 on failure, neither of which have
// content, we don't json-ify the response
.then((response) => {
if (response.ok) {
dispatch(deleteAssetSuccess(assetId, response));
dispatch(deleteAssetSuccess(assetId));
} else {
dispatch(assetDeleteFailure(response));
dispatch(deleteAssetFailure(assetId));
}

@@ -101,16 +108,16 @@ });

dispatch(togglingLockAsset(asset));
clientApi.requestToggleLockAsset(courseDetails.id, asset)
return clientApi.requestToggleLockAsset(courseDetails.id, asset)
.then((response) => {
if (response.ok) {
dispatch(toggleLockAssetSuccess(asset));
} else {
dispatch(toggleLockAssetFailure(asset, response));
if (!response.ok) {
throw new Error(response);
}
})
.then(() => {
dispatch(toggleLockAssetSuccess(asset));
})
.catch((error) => {
dispatch(toggleLockAssetFailure(asset, error));
});
};
export const clearAssetsStatus = () =>
dispatch =>
dispatch({ type: assetActions.CLEAR_ASSETS_STATUS });
export const uploadingAssets = count => ({

@@ -117,0 +124,0 @@ type: assetActions.UPLOADING_ASSETS,

@@ -7,17 +7,15 @@ import configureStore from 'redux-mock-store';

import * as actionCreators from './assets';
import { requestInitial } from '../reducers/assets';
import { assetActions } from '../../data/constants/actionTypes';
const initialState = {
request: {
assetTypes: {},
start: 0,
end: 0,
page: 0,
pageSize: 50,
totalCount: 0,
sort: 'date_added',
direction: 'desc',
},
request: { ...requestInitial },
};
const courseDetails = {
id: 'edX',
};
const assetId = 'asset';
const assetsEndpoint = endpoints.assets;

@@ -28,3 +26,3 @@ const middlewares = [thunk];

describe('Assets Action Creator', () => {
describe('Assets Action Creators', () => {
beforeEach(() => {

@@ -34,2 +32,7 @@ store = mockStore(initialState);

afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it('returns expected state from requestAssetsSuccess', () => {

@@ -40,24 +43,35 @@ const expectedAction = { data: 'response', type: assetActions.REQUEST_ASSETS_SUCCESS };

it('returns expected state from assetDeleteFailure', () => {
const expectedAction = { response: 'response', type: assetActions.DELETE_ASSET_FAILURE };
expect(store.dispatch(actionCreators.assetDeleteFailure('response'))).toEqual(expectedAction);
const expectedAction = { assetId, type: assetActions.DELETE_ASSET_FAILURE };
expect(store.dispatch(actionCreators.deleteAssetFailure(assetId))).toEqual(expectedAction);
});
it('returns expected state from getAssets success', () => {
const request = {
page: 0,
assetTypes: {},
sort: 'date_added',
direction: 'desc',
};
const request = requestInitial;
const response = request;
const courseDetails = {
id: 'edX',
fetchMock.once(`begin:${assetsEndpoint}`, response);
const expectedActions = [
{ type: assetActions.REQUEST_ASSETS_SUCCESS, data: response },
];
return store.dispatch(actionCreators.getAssets(request, courseDetails)).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions);
});
});
it('returns expected state from getAssets failure', () => {
const request = requestInitial;
const response = {
status: 400,
body: request,
};
const errorResponse = new Error(response);
fetchMock.once(`begin:${assetsEndpoint}`, response);
const expectedActions = [
{ type: assetActions.REQUEST_ASSETS_SUCCESS, data: response },
{ type: assetActions.REQUEST_ASSETS_FAILURE, data: errorResponse },
];
store = mockStore(initialState);
return store.dispatch(actionCreators.getAssets(request, courseDetails)).then(() => {

@@ -68,2 +82,20 @@ // return of async actions

});
it('returns expected state from getAssets if response metadata does not match request', () => {
const request = {
...requestInitial,
direction: 'asc',
};
const response = request;
fetchMock.once(`begin:${assetsEndpoint}`, response);
// if the response is not the same as the request, we expect nothing
const expectedActions = [];
return store.dispatch(actionCreators.getAssets(request, courseDetails)).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions);
});
});
it('returns expected state from filterUpdate', () => {

@@ -81,6 +113,35 @@ const expectedAction = { data: { filter: true }, type: assetActions.FILTER_UPDATED };

});
it('returns expected state from deleteAssetSuccess', () => {
const expectedAction = { assetId: 'assetId', response: 'response', type: assetActions.DELETE_ASSET_SUCCESS };
expect(store.dispatch(actionCreators.deleteAssetSuccess('assetId', 'response'))).toEqual(expectedAction);
it('returns expected state from deleteAsset success', () => {
const response = {
status: 204,
body: {},
};
fetchMock.once(`begin:${assetsEndpoint}`, response);
const expectedActions = [
{ type: assetActions.DELETE_ASSET_SUCCESS, assetId },
];
return store.dispatch(actionCreators.deleteAsset(assetId, courseDetails)).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions);
});
});
it('returns expected state from deleteAsset failure', () => {
const response = {
status: 400,
};
fetchMock.once(`begin:${assetsEndpoint}`, response);
const expectedActions = [
{ type: assetActions.DELETE_ASSET_FAILURE, assetId },
];
return store.dispatch(actionCreators.deleteAsset(assetId, courseDetails)).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions);
});
});
it('returns expected state from togglingLockAsset', () => {

@@ -87,0 +148,0 @@ const expectedAction = { asset: 'asset', type: assetActions.TOGGLING_LOCK_ASSET_SUCCESS };

/* ASSETS ACTION TYPES */
export const assetActions = {
REQUEST_ASSETS_SUCCESS: 'REQUEST_ASSETS_SUCCESS',
REQUEST_ASSETS_FAILURE: 'REQUEST_ASSETS_FAILURE',
FILTER_UPDATED: 'FILTER_UPDATED',

@@ -5,0 +6,0 @@ DELETE_ASSET_SUCCESS: 'DELETE_ASSET_SUCCESS',

@@ -9,7 +9,7 @@ import { combineReducers } from 'redux';

const filtersInitial = {
export const filtersInitial = {
assetTypes: {},
};
const paginationInitial = {
export const paginationInitial = {
start: 0,

@@ -22,3 +22,3 @@ end: 0,

const sortInitial = {
export const sortInitial = {
sort: 'date_added',

@@ -28,3 +28,3 @@ direction: 'desc',

const requestInitial = {
export const requestInitial = {
...filtersInitial,

@@ -36,3 +36,3 @@ ...paginationInitial,

const filters = (state = filtersInitial, action) => {
export const filters = (state = filtersInitial, action) => {
switch (action.type) {

@@ -67,3 +67,3 @@ case assetActions.REQUEST_ASSETS_SUCCESS:

const pagination = (state = paginationInitial, action) => {
export const pagination = (state = paginationInitial, action) => {
switch (action.type) {

@@ -83,3 +83,3 @@ case assetActions.REQUEST_ASSETS_SUCCESS:

const sort = (state = sortInitial, action) => {
export const sort = (state = sortInitial, action) => {
switch (action.type) {

@@ -96,4 +96,14 @@ case assetActions.REQUEST_ASSETS_SUCCESS:

const status = (state = {}, action) => {
export const status = (state = {}, action) => {
switch (action.type) {
case assetActions.REQUEST_ASSETS_SUCCESS:
return {
response: action.response,
type: action.type,
};
case assetActions.REQUEST_ASSETS_FAILURE:
return {
response: action.response,
type: action.type,
};
case assetActions.CLEAR_ASSETS_STATUS:

@@ -100,0 +110,0 @@ return {};

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc