Merge remote-tracking branch 'parent/main' into kb_migration
This commit is contained in:
commit
1daa5d9096
42 changed files with 1235 additions and 255 deletions
|
@ -21,12 +21,6 @@ Layout/ArgumentAlignment:
|
|||
- 'config/initializers/cors.rb'
|
||||
- 'config/initializers/session_store.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
|
||||
Layout/ExtraSpacing:
|
||||
Exclude:
|
||||
- 'config/initializers/omniauth.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
|
||||
# SupportedHashRocketStyles: key, separator, table
|
||||
|
@ -39,12 +33,6 @@ Layout/HashAlignment:
|
|||
- 'config/initializers/rack_attack.rb'
|
||||
- 'config/routes.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: Width, AllowedPatterns.
|
||||
Layout/IndentationWidth:
|
||||
Exclude:
|
||||
- 'config/initializers/ffmpeg.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment.
|
||||
Layout/LeadingCommentSpace:
|
||||
|
@ -52,14 +40,6 @@ Layout/LeadingCommentSpace:
|
|||
- 'config/application.rb'
|
||||
- 'config/initializers/omniauth.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
|
||||
# SupportedStyles: space, no_space
|
||||
# SupportedStylesForEmptyBraces: space, no_space
|
||||
Layout/SpaceBeforeBlockBraces:
|
||||
Exclude:
|
||||
- 'config/initializers/paperclip.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: require_no_space, require_space
|
||||
|
@ -68,19 +48,6 @@ Layout/SpaceInLambdaLiteral:
|
|||
- 'config/environments/production.rb'
|
||||
- 'config/initializers/content_security_policy.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: space, no_space
|
||||
Layout/SpaceInsideStringInterpolation:
|
||||
Exclude:
|
||||
- 'config/initializers/webauthn.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowInHeredoc.
|
||||
Layout/TrailingWhitespace:
|
||||
Exclude:
|
||||
- 'config/initializers/paperclip.rb'
|
||||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
Lint/AmbiguousBlockAssociation:
|
||||
Exclude:
|
||||
|
@ -618,7 +585,6 @@ RSpec/NoExpectationExample:
|
|||
|
||||
RSpec/PendingWithoutReason:
|
||||
Exclude:
|
||||
- 'spec/controllers/statuses_controller_spec.rb'
|
||||
- 'spec/models/account_spec.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
|
@ -634,10 +600,6 @@ RSpec/RepeatedExample:
|
|||
Exclude:
|
||||
- 'spec/policies/status_policy_spec.rb'
|
||||
|
||||
RSpec/RepeatedExampleGroupBody:
|
||||
Exclude:
|
||||
- 'spec/controllers/statuses_controller_spec.rb'
|
||||
|
||||
RSpec/StubbedMock:
|
||||
Exclude:
|
||||
- 'spec/controllers/api/base_controller_spec.rb'
|
||||
|
|
|
@ -29,7 +29,7 @@ class Api::V1::Admin::DomainAllowsController < Api::BaseController
|
|||
def create
|
||||
authorize :domain_allow, :create?
|
||||
|
||||
@domain_allow = DomainAllow.find_by(resource_params)
|
||||
@domain_allow = DomainAllow.find_by(domain: resource_params[:domain])
|
||||
|
||||
if @domain_allow.nil?
|
||||
@domain_allow = DomainAllow.create!(resource_params)
|
||||
|
|
|
@ -13,7 +13,7 @@ class Api::V1::FeaturedTagsController < Api::BaseController
|
|||
end
|
||||
|
||||
def create
|
||||
featured_tag = CreateFeaturedTagService.new.call(current_account, featured_tag_params[:name])
|
||||
featured_tag = CreateFeaturedTagService.new.call(current_account, params.require(:name))
|
||||
render json: featured_tag, serializer: REST::FeaturedTagSerializer
|
||||
end
|
||||
|
||||
|
@ -33,6 +33,6 @@ class Api::V1::FeaturedTagsController < Api::BaseController
|
|||
end
|
||||
|
||||
def featured_tag_params
|
||||
params.permit(:name)
|
||||
params.require(:name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -127,7 +127,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def set_sessions
|
||||
@sessions = current_user.session_activations
|
||||
@sessions = current_user.session_activations.order(updated_at: :desc)
|
||||
end
|
||||
|
||||
def set_strikes
|
||||
|
|
|
@ -9,6 +9,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
|
|||
before_action :set_body_classes
|
||||
before_action :set_cache_headers
|
||||
|
||||
before_action :set_last_used_at_by_app, only: :index, unless: -> { request.format == :json }
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
||||
include Localized
|
||||
|
@ -35,4 +37,14 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
|
|||
def set_cache_headers
|
||||
response.cache_control.replace(private: true, no_store: true)
|
||||
end
|
||||
|
||||
def set_last_used_at_by_app
|
||||
@last_used_at_by_app = Doorkeeper::AccessToken
|
||||
.select('DISTINCT ON (application_id) application_id, last_used_at')
|
||||
.where(resource_owner_id: current_resource_owner.id)
|
||||
.where.not(last_used_at: nil)
|
||||
.order(application_id: :desc, last_used_at: :desc)
|
||||
.pluck(:application_id, :last_used_at)
|
||||
.to_h
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
|
|||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import { scrollTop } from '../scroll';
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
|
||||
export default class Column extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -35,17 +37,17 @@ export default class Column extends React.PureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
if (this.props.bindToDocument) {
|
||||
document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
|
||||
document.addEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
} else {
|
||||
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
|
||||
this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.props.bindToDocument) {
|
||||
document.removeEventListener('wheel', this.handleWheel);
|
||||
document.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
} else {
|
||||
this.node.removeEventListener('wheel', this.handleWheel);
|
||||
this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { supportsPassiveEvents } from 'detect-passive-events';
|
|||
import classNames from 'classnames';
|
||||
import { CircularProgress } from 'mastodon/components/loading_indicator';
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
|
||||
let id = 0;
|
||||
|
||||
class DropdownMenu extends React.PureComponent {
|
||||
|
@ -35,12 +35,13 @@ class DropdownMenu extends React.PureComponent {
|
|||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
document.addEventListener('keydown', this.handleKeyDown, false);
|
||||
document.addEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.addEventListener('keydown', this.handleKeyDown, { capture: true });
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
|
||||
if (this.focusedItem && this.props.openedViaKeyboard) {
|
||||
|
@ -49,8 +50,8 @@ class DropdownMenu extends React.PureComponent {
|
|||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
document.removeEventListener('click', this.handleDocumentClick, false);
|
||||
document.removeEventListener('keydown', this.handleKeyDown, false);
|
||||
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.removeEventListener('keydown', this.handleKeyDown, { capture: true });
|
||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ import { connect } from 'react-redux';
|
|||
|
||||
const MOUSE_IDLE_DELAY = 300;
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
|
||||
const mapStateToProps = (state, { scrollKey }) => {
|
||||
return {
|
||||
preventScroll: scrollKey === state.getIn(['dropdown_menu', 'scroll_key']),
|
||||
|
@ -237,20 +239,20 @@ class ScrollableList extends PureComponent {
|
|||
attachScrollListener () {
|
||||
if (this.props.bindToDocument) {
|
||||
document.addEventListener('scroll', this.handleScroll);
|
||||
document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined);
|
||||
document.addEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
} else {
|
||||
this.node.addEventListener('scroll', this.handleScroll);
|
||||
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined);
|
||||
this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
detachScrollListener () {
|
||||
if (this.props.bindToDocument) {
|
||||
document.removeEventListener('scroll', this.handleScroll);
|
||||
document.removeEventListener('wheel', this.handleWheel);
|
||||
document.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
} else {
|
||||
this.node.removeEventListener('scroll', this.handleScroll);
|
||||
this.node.removeEventListener('wheel', this.handleWheel);
|
||||
this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { PureComponent, Fragment } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createPortal } from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import { fromJS } from 'immutable';
|
||||
|
@ -95,7 +95,7 @@ export default class MediaContainer extends PureComponent {
|
|||
}),
|
||||
});
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
return createPortal(
|
||||
<Component {...props} key={`media-${i}`} />,
|
||||
component,
|
||||
);
|
||||
|
|
|
@ -27,7 +27,7 @@ const messages = defineMessages({
|
|||
|
||||
let EmojiPicker, Emoji; // load asynchronously
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
|
||||
|
||||
const backgroundImageFn = () => `${assetHost}/emoji/sheet_13.png`;
|
||||
|
||||
|
@ -78,12 +78,12 @@ class ModifierPickerMenu extends React.PureComponent {
|
|||
};
|
||||
|
||||
attachListeners () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
document.addEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
}
|
||||
|
||||
removeListeners () {
|
||||
document.removeEventListener('click', this.handleDocumentClick, false);
|
||||
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ class EmojiPickerMenuImpl extends React.PureComponent {
|
|||
};
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
document.addEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
|
||||
// Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
|
||||
|
@ -191,7 +191,7 @@ class EmojiPickerMenuImpl extends React.PureComponent {
|
|||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
document.removeEventListener('click', this.handleDocumentClick, false);
|
||||
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ const messages = defineMessages({
|
|||
clear: { id: 'emoji_button.clear', defaultMessage: 'Clear' },
|
||||
});
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
|
||||
|
||||
class LanguageDropdownMenu extends React.PureComponent {
|
||||
|
||||
|
@ -39,11 +39,12 @@ class LanguageDropdownMenu extends React.PureComponent {
|
|||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
document.addEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
|
||||
// Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
|
||||
|
@ -57,7 +58,7 @@ class LanguageDropdownMenu extends React.PureComponent {
|
|||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
document.removeEventListener('click', this.handleDocumentClick, false);
|
||||
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ const messages = defineMessages({
|
|||
change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
|
||||
});
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
|
||||
|
||||
class PrivacyDropdownMenu extends React.PureComponent {
|
||||
|
||||
|
@ -36,6 +36,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
|||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -93,13 +94,13 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
|||
};
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
document.addEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
if (this.focusedItem) this.focusedItem.focus({ preventScroll: true });
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
document.removeEventListener('click', this.handleDocumentClick, false);
|
||||
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ class EmbedModal extends ImmutablePureComponent {
|
|||
className='embed-modal__iframe'
|
||||
frameBorder='0'
|
||||
ref={this.setIframeRef}
|
||||
sandbox='allow-same-origin'
|
||||
sandbox='allow-scripts allow-same-origin'
|
||||
title='preview'
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { setupBrowserNotifications } from 'mastodon/actions/notifications';
|
||||
import Mastodon from 'mastodon/containers/mastodon';
|
||||
import { store } from 'mastodon/store';
|
||||
|
@ -17,7 +17,8 @@ function main() {
|
|||
const mountNode = document.getElementById('mastodon');
|
||||
const props = JSON.parse(mountNode.getAttribute('data-props'));
|
||||
|
||||
ReactDOM.render(<Mastodon {...props} />, mountNode);
|
||||
const root = createRoot(mountNode);
|
||||
root.render(<Mastodon {...props} />);
|
||||
store.dispatch(setupBrowserNotifications());
|
||||
|
||||
if (process.env.NODE_ENV === 'production' && me && 'serviceWorker' in navigator) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import './public-path';
|
|||
import { delegate } from '@rails/ujs';
|
||||
import ready from '../mastodon/ready';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
const setAnnouncementEndsAttributes = (target) => {
|
||||
const valid = target?.value && target?.validity?.valid;
|
||||
|
@ -241,11 +241,13 @@ ready(() => {
|
|||
|
||||
import('../mastodon/containers/admin_component').then(({ default: AdminComponent }) => {
|
||||
return import('../mastodon/components/admin/' + componentName).then(({ default: Component }) => {
|
||||
ReactDOM.render((
|
||||
const root = createRoot(element);
|
||||
|
||||
root.render (
|
||||
<AdminComponent locale={locale}>
|
||||
<Component {...componentProps} />
|
||||
</AdminComponent>
|
||||
), element);
|
||||
</AdminComponent>,
|
||||
);
|
||||
});
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
|
|
|
@ -15,7 +15,7 @@ import { delegate } from '@rails/ujs';
|
|||
import emojify from '../mastodon/features/emoji/emoji';
|
||||
import { getLocale } from '../mastodon/locales';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { createBrowserHistory } from 'history';
|
||||
|
||||
start();
|
||||
|
@ -153,7 +153,8 @@ function loaded() {
|
|||
|
||||
const content = document.createElement('div');
|
||||
|
||||
ReactDOM.render(<MediaContainer locale={locale} components={reactComponents} />, content);
|
||||
const root = createRoot(content);
|
||||
root.render(<MediaContainer locale={locale} components={reactComponents} />);
|
||||
document.body.appendChild(content);
|
||||
scrollToDetailedStatus();
|
||||
})
|
||||
|
|
|
@ -4,7 +4,7 @@ import { start } from '../mastodon/common';
|
|||
import ready from '../mastodon/ready';
|
||||
import ComposeContainer from '../mastodon/containers/compose_container';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
start();
|
||||
|
||||
|
@ -16,7 +16,8 @@ function loaded() {
|
|||
if(!attr) return;
|
||||
|
||||
const props = JSON.parse(attr);
|
||||
ReactDOM.render(<ComposeContainer {...props} />, mountNode);
|
||||
const root = createRoot(mountNode);
|
||||
root.render(<ComposeContainer {...props} />);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
5
app/javascript/types/image.d.ts
vendored
5
app/javascript/types/image.d.ts
vendored
|
@ -14,11 +14,6 @@ declare module '*.jpg' {
|
|||
export default path;
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const path: string;
|
||||
export default path;
|
||||
}
|
||||
|
||||
declare module '*.png' {
|
||||
const path: string;
|
||||
export default path;
|
||||
|
|
|
@ -16,7 +16,7 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity
|
|||
@account,
|
||||
target_account,
|
||||
status_ids: target_statuses.nil? ? [] : target_statuses.map(&:id),
|
||||
comment: @json['content'] || '',
|
||||
comment: report_comment,
|
||||
uri: report_uri
|
||||
)
|
||||
end
|
||||
|
@ -35,4 +35,8 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity
|
|||
def report_uri
|
||||
@json['id'] unless @json['id'].nil? || non_matching_uri_hosts?(@account.uri, @json['id'])
|
||||
end
|
||||
|
||||
def report_comment
|
||||
(@json['content'] || '')[0...5000]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,6 +28,8 @@ class ActivityPub::TagManager
|
|||
return activity_account_status_url(target.account, target) if target.reblog?
|
||||
|
||||
short_account_status_url(target.account, target)
|
||||
when :flag
|
||||
target.uri
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -47,6 +49,8 @@ class ActivityPub::TagManager
|
|||
emoji_url(target)
|
||||
when :emoji_reaction
|
||||
emoji_reaction_url(target)
|
||||
when :flag
|
||||
target.uri
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -9,10 +9,6 @@ module ApplicationExtension
|
|||
validates :redirect_uri, length: { maximum: 2_000 }
|
||||
end
|
||||
|
||||
def most_recently_used_access_token
|
||||
@most_recently_used_access_token ||= access_tokens.where.not(last_used_at: nil).order(last_used_at: :desc).first
|
||||
end
|
||||
|
||||
def confirmation_redirect_uri
|
||||
redirect_uri.lines.first.strip
|
||||
end
|
||||
|
|
|
@ -140,7 +140,7 @@ class LinkDetailsExtractor
|
|||
end
|
||||
|
||||
def html
|
||||
player_url.present? ? content_tag(:iframe, nil, src: player_url, width: width, height: height, allowtransparency: 'true', scrolling: 'no', frameborder: '0') : nil
|
||||
player_url.present? ? content_tag(:iframe, nil, src: player_url, width: width, height: height, allowfullscreen: 'true', allowtransparency: 'true', scrolling: 'no', frameborder: '0') : nil
|
||||
end
|
||||
|
||||
def width
|
||||
|
|
|
@ -40,7 +40,10 @@ class Report < ApplicationRecord
|
|||
scope :resolved, -> { where.not(action_taken_at: nil) }
|
||||
scope :with_accounts, -> { includes([:account, :target_account, :action_taken_by_account, :assigned_account].index_with({ user: [:invite_request, :invite] })) }
|
||||
|
||||
validates :comment, length: { maximum: 1_000 }
|
||||
# A report is considered local if the reporter is local
|
||||
delegate :local?, to: :account
|
||||
|
||||
validates :comment, length: { maximum: 1_000 }, if: :local?
|
||||
validates :rule_ids, absence: true, unless: :violation?
|
||||
|
||||
validate :validate_rule_ids
|
||||
|
@ -51,10 +54,6 @@ class Report < ApplicationRecord
|
|||
violation: 2_000,
|
||||
}
|
||||
|
||||
def local?
|
||||
false # Force uri_for to use uri attribute
|
||||
end
|
||||
|
||||
before_validation :set_uri, only: :create
|
||||
|
||||
after_create_commit :trigger_webhooks
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
= render 'auth/shared/progress', stage: 'confirm'
|
||||
|
||||
= hidden_field_tag :confirmation_token, params[:confirmation_token]
|
||||
= hidden_field_tag :redirect_to_app, params[:redirect_to_app]
|
||||
|
||||
%p.lead= t('auth.captcha_confirmation.hint_html')
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
.announcements-list__item__action-bar
|
||||
.announcements-list__item__meta
|
||||
- if application.most_recently_used_access_token
|
||||
= t('doorkeeper.authorized_applications.index.last_used_at', date: l(application.most_recently_used_access_token.last_used_at.to_date))
|
||||
- if @last_used_at_by_app[application.id]
|
||||
= t('doorkeeper.authorized_applications.index.last_used_at', date: l(@last_used_at_by_app[application.id].to_date))
|
||||
- else
|
||||
= t('doorkeeper.authorized_applications.index.never_used')
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
if ENV['FFMPEG_BINARY'].present?
|
||||
FFMPEG.ffmpeg_binary = ENV['FFMPEG_BINARY']
|
||||
FFMPEG.ffmpeg_binary = ENV['FFMPEG_BINARY']
|
||||
end
|
||||
|
|
|
@ -73,7 +73,7 @@ Devise.setup do |config|
|
|||
oidc_options[:display_name] = ENV['OIDC_DISPLAY_NAME'] #OPTIONAL
|
||||
oidc_options[:issuer] = ENV['OIDC_ISSUER'] if ENV['OIDC_ISSUER'] #NEED
|
||||
oidc_options[:discovery] = ENV['OIDC_DISCOVERY'] == 'true' if ENV['OIDC_DISCOVERY'] #OPTIONAL (default: false)
|
||||
oidc_options[:client_auth_method] = ENV['OIDC_CLIENT_AUTH_METHOD'] if ENV['OIDC_CLIENT_AUTH_METHOD'] #OPTIONAL (default: basic)
|
||||
oidc_options[:client_auth_method] = ENV['OIDC_CLIENT_AUTH_METHOD'] if ENV['OIDC_CLIENT_AUTH_METHOD'] #OPTIONAL (default: basic)
|
||||
scope_string = ENV['OIDC_SCOPE'] if ENV['OIDC_SCOPE'] #NEED
|
||||
scopes = scope_string.split(',')
|
||||
oidc_options[:scope] = scopes.map { |x| x.to_sym }
|
||||
|
|
|
@ -61,13 +61,13 @@ if ENV['S3_ENABLED'] == 'true'
|
|||
|
||||
s3_options: {
|
||||
signature_version: ENV.fetch('S3_SIGNATURE_VERSION') { 'v4' },
|
||||
http_open_timeout: ENV.fetch('S3_OPEN_TIMEOUT'){ '5' }.to_i,
|
||||
http_read_timeout: ENV.fetch('S3_READ_TIMEOUT'){ '5' }.to_i,
|
||||
http_open_timeout: ENV.fetch('S3_OPEN_TIMEOUT') { '5' }.to_i,
|
||||
http_read_timeout: ENV.fetch('S3_READ_TIMEOUT') { '5' }.to_i,
|
||||
http_idle_timeout: 5,
|
||||
retry_limit: 0,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Paperclip::Attachment.default_options[:s3_permissions] = ->(*) { nil } if ENV['S3_PERMISSION'] == ''
|
||||
|
||||
if ENV.has_key?('S3_ENDPOINT')
|
||||
|
@ -124,7 +124,7 @@ elsif ENV['SWIFT_ENABLED'] == 'true'
|
|||
openstack_cache_ttl: ENV.fetch('SWIFT_CACHE_TTL') { 60 },
|
||||
openstack_temp_url_key: ENV['SWIFT_TEMP_URL_KEY'],
|
||||
},
|
||||
|
||||
|
||||
fog_file: { 'Cache-Control' => 'public, max-age=315576000, immutable' },
|
||||
|
||||
fog_directory: ENV['SWIFT_CONTAINER'],
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
WebAuthn.configure do |config|
|
||||
# This value needs to match `window.location.origin` evaluated by
|
||||
# the User Agent during registration and authentication ceremonies.
|
||||
config.origin = "#{Rails.configuration.x.use_https ? 'https' : 'http' }://#{Rails.configuration.x.web_domain}"
|
||||
config.origin = "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}"
|
||||
|
||||
# Relying Party name for display purposes
|
||||
config.rp_name = "Mastodon"
|
||||
|
|
|
@ -9,7 +9,6 @@ const config = {
|
|||
'<rootDir>/public/',
|
||||
'<rootDir>/tmp/',
|
||||
],
|
||||
setupFiles: ['raf/polyfill'],
|
||||
setupFilesAfterEnv: ['<rootDir>/app/javascript/mastodon/test_setup.js'],
|
||||
collectCoverageFrom: [
|
||||
'app/javascript/mastodon/**/*.{js,jsx,ts,tsx}',
|
||||
|
|
15
package.json
15
package.json
|
@ -87,8 +87,8 @@
|
|||
"postcss-loader": "^4.3.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"punycode": "^2.3.0",
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-hotkeys": "^1.1.4",
|
||||
"react-immutable-proptypes": "^2.2.0",
|
||||
|
@ -140,7 +140,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@types/babel__core": "^7.20.0",
|
||||
"@types/emoji-mart": "^3.0.9",
|
||||
"@types/escape-html": "^1.0.2",
|
||||
|
@ -155,9 +155,8 @@
|
|||
"@types/pg": "^8.6.6",
|
||||
"@types/prop-types": "^15.7.5",
|
||||
"@types/punycode": "^2.1.0",
|
||||
"@types/raf": "^3.4.0",
|
||||
"@types/react": "^16.14.38",
|
||||
"@types/react-dom": "^16.9.18",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"@types/react-helmet": "^6.1.6",
|
||||
"@types/react-immutable-proptypes": "^2.1.0",
|
||||
"@types/react-intl": "2.3.18",
|
||||
|
@ -195,9 +194,8 @@
|
|||
"jest-environment-jsdom": "^29.5.0",
|
||||
"lint-staged": "^13.2.2",
|
||||
"prettier": "^2.8.8",
|
||||
"raf": "^3.4.1",
|
||||
"react-intl-translations-manager": "^5.0.3",
|
||||
"react-test-renderer": "^16.14.0",
|
||||
"react-test-renderer": "^18.2.0",
|
||||
"stylelint": "^15.6.1",
|
||||
"stylelint-config-standard-scss": "^9.0.0",
|
||||
"typescript": "^5.0.4",
|
||||
|
@ -205,6 +203,7 @@
|
|||
"yargs": "^17.7.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "^18.0.26",
|
||||
"kind-of": "^6.0.3",
|
||||
"webpack/terser-webpack-plugin": "^4.2.3"
|
||||
},
|
||||
|
|
|
@ -5,23 +5,182 @@ require 'rails_helper'
|
|||
describe Api::V1::Admin::CanonicalEmailBlocksController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||
let(:user) { Fabricate(:user, role: role) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'admin:read:canonical_email_blocks admin:write:canonical_email_blocks' }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
shared_examples 'forbidden for wrong scope' do |wrong_scope|
|
||||
let(:scopes) { wrong_scope }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'forbidden for wrong role' do |wrong_role|
|
||||
let(:role) { UserRole.find_by(name: wrong_role) }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
get :index
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
get :index
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :index, params: { account_id: account.id, limit: 2 }
|
||||
get :index
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
context 'when there is no canonical email block' do
|
||||
it 'returns an empty list' do
|
||||
get :index
|
||||
|
||||
body = body_as_json
|
||||
|
||||
expect(body).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are canonical email blocks' do
|
||||
let!(:canonical_email_blocks) { Fabricate.times(5, :canonical_email_block) }
|
||||
let(:expected_email_hashes) { canonical_email_blocks.pluck(:canonical_email_hash) }
|
||||
|
||||
it 'returns the correct canonical email hashes' do
|
||||
get :index
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json.pluck(:canonical_email_hash)).to match_array(expected_email_hashes)
|
||||
end
|
||||
|
||||
context 'with limit param' do
|
||||
let(:params) { { limit: 2 } }
|
||||
|
||||
it 'returns only the requested number of canonical email blocks' do
|
||||
get :index, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json.size).to eq(params[:limit])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with since_id param' do
|
||||
let(:params) { { since_id: canonical_email_blocks[1].id } }
|
||||
|
||||
it 'returns only the canonical email blocks after since_id' do
|
||||
get :index, params: params
|
||||
|
||||
canonical_email_blocks_ids = canonical_email_blocks.pluck(:id).map(&:to_s)
|
||||
json = body_as_json
|
||||
|
||||
expect(json.pluck(:id)).to match_array(canonical_email_blocks_ids[2..])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with max_id param' do
|
||||
let(:params) { { max_id: canonical_email_blocks[3].id } }
|
||||
|
||||
it 'returns only the canonical email blocks before max_id' do
|
||||
get :index, params: params
|
||||
|
||||
canonical_email_blocks_ids = canonical_email_blocks.pluck(:id).map(&:to_s)
|
||||
json = body_as_json
|
||||
|
||||
expect(json.pluck(:id)).to match_array(canonical_email_blocks_ids[..2])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
let!(:canonical_email_block) { Fabricate(:canonical_email_block) }
|
||||
let(:params) { { id: canonical_email_block.id } }
|
||||
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
get :show, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
get :show, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
context 'when canonical email block exists' do
|
||||
it 'returns http success' do
|
||||
get :show, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns canonical email block data correctly' do
|
||||
get :show, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:id]).to eq(canonical_email_block.id.to_s)
|
||||
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when canonical block does not exist' do
|
||||
it 'returns http not found' do
|
||||
get :show, params: { id: 0 }
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #test' do
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
post :test
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
post :test, params: { email: 'whatever@email.com' }
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
context 'when required email is not provided' do
|
||||
it 'returns http bad request' do
|
||||
post :test
|
||||
|
@ -68,4 +227,132 @@ describe Api::V1::Admin::CanonicalEmailBlocksController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:params) { { email: 'example@email.com' } }
|
||||
let(:canonical_email_block) { CanonicalEmailBlock.new(email: params[:email]) }
|
||||
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
post :create, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
post :create, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns canonical_email_hash correctly' do
|
||||
post :create, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
|
||||
end
|
||||
|
||||
context 'when required email param is not provided' do
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when canonical_email_hash param is provided instead of email' do
|
||||
let(:params) { { canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } }
|
||||
|
||||
it 'returns http success' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns correct canonical_email_hash' do
|
||||
post :create, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:canonical_email_hash]).to eq(params[:canonical_email_hash])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when both email and canonical_email_hash params are provided' do
|
||||
let(:params) { { email: 'example@email.com', canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } }
|
||||
|
||||
it 'returns http success' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'ignores canonical_email_hash param' do
|
||||
post :create, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when canonical email was already blocked' do
|
||||
before do
|
||||
canonical_email_block.save
|
||||
end
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
let!(:canonical_email_block) { Fabricate(:canonical_email_block) }
|
||||
let(:params) { { id: canonical_email_block.id } }
|
||||
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
delete :destroy, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
delete :destroy, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
delete :destroy, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
context 'when canonical email block is not found' do
|
||||
it 'returns http not found' do
|
||||
delete :destroy, params: { id: 0 }
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -128,5 +128,13 @@ RSpec.describe Api::V1::Admin::DomainAllowsController do
|
|||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when domain name is not specified' do
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,19 +5,280 @@ require 'rails_helper'
|
|||
describe Api::V1::Admin::EmailDomainBlocksController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||
let(:user) { Fabricate(:user, role: role) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:scopes) { 'admin:read:email_domain_blocks admin:write:email_domain_blocks' }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
shared_examples 'forbidden for wrong scope' do |wrong_scope|
|
||||
let(:scopes) { wrong_scope }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'forbidden for wrong role' do |wrong_role|
|
||||
let(:role) { UserRole.find_by(name: wrong_role) }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
get :index
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
get :index
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :index, params: { account_id: account.id, limit: 2 }
|
||||
get :index
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
context 'when there is no email domain block' do
|
||||
it 'returns an empty list' do
|
||||
get :index
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are email domain blocks' do
|
||||
let!(:email_domain_blocks) { Fabricate.times(5, :email_domain_block) }
|
||||
let(:blocked_email_domains) { email_domain_blocks.pluck(:domain) }
|
||||
|
||||
it 'return the correct blocked email domains' do
|
||||
get :index
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json.pluck(:domain)).to match_array(blocked_email_domains)
|
||||
end
|
||||
|
||||
context 'with limit param' do
|
||||
let(:params) { { limit: 2 } }
|
||||
|
||||
it 'returns only the requested number of email domain blocks' do
|
||||
get :index, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json.size).to eq(params[:limit])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with since_id param' do
|
||||
let(:params) { { since_id: email_domain_blocks[1].id } }
|
||||
|
||||
it 'returns only the email domain blocks after since_id' do
|
||||
get :index, params: params
|
||||
|
||||
email_domain_blocks_ids = email_domain_blocks.pluck(:id).map(&:to_s)
|
||||
json = body_as_json
|
||||
|
||||
expect(json.pluck(:id)).to match_array(email_domain_blocks_ids[2..])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with max_id param' do
|
||||
let(:params) { { max_id: email_domain_blocks[3].id } }
|
||||
|
||||
it 'returns only the email domain blocks before max_id' do
|
||||
get :index, params: params
|
||||
|
||||
email_domain_blocks_ids = email_domain_blocks.pluck(:id).map(&:to_s)
|
||||
json = body_as_json
|
||||
|
||||
expect(json.pluck(:id)).to match_array(email_domain_blocks_ids[..2])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
let!(:email_domain_block) { Fabricate(:email_domain_block) }
|
||||
let(:params) { { id: email_domain_block.id } }
|
||||
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
get :show, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
get :show, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
context 'when email domain block exists' do
|
||||
it 'returns http success' do
|
||||
get :show, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns the correct blocked domain' do
|
||||
get :show, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:domain]).to eq(email_domain_block.domain)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when email domain block does not exist' do
|
||||
it 'returns http not found' do
|
||||
get :show, params: { id: 0 }
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:params) { { domain: 'example.com' } }
|
||||
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
post :create, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
post :create, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns the correct blocked email domain' do
|
||||
post :create, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:domain]).to eq(params[:domain])
|
||||
end
|
||||
|
||||
context 'when domain param is not provided' do
|
||||
let(:params) { { domain: '' } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provided domain name has an invalid character' do
|
||||
let(:params) { { domain: 'do\uD800.com' } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provided domain is already blocked' do
|
||||
before do
|
||||
EmailDomainBlock.create(params)
|
||||
end
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
let!(:email_domain_block) { Fabricate(:email_domain_block) }
|
||||
let(:params) { { id: email_domain_block.id } }
|
||||
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
delete :destroy, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
delete :destroy, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
delete :destroy, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns an empty body' do
|
||||
delete :destroy, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json).to be_empty
|
||||
end
|
||||
|
||||
it 'deletes email domain block' do
|
||||
delete :destroy, params: params
|
||||
|
||||
email_domain_block = EmailDomainBlock.find_by(id: params[:id])
|
||||
|
||||
expect(email_domain_block).to be_nil
|
||||
end
|
||||
|
||||
context 'when email domain block does not exist' do
|
||||
it 'returns http not found' do
|
||||
delete :destroy, params: { id: 0 }
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,19 +5,305 @@ require 'rails_helper'
|
|||
describe Api::V1::Admin::IpBlocksController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||
let(:user) { Fabricate(:user, role: role) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'admin:read:ip_blocks admin:write:ip_blocks' }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
shared_examples 'forbidden for wrong scope' do |wrong_scope|
|
||||
let(:scopes) { wrong_scope }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'forbidden for wrong role' do |wrong_role|
|
||||
let(:role) { UserRole.find_by(name: wrong_role) }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
get :index
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'admin:write:ip_blocks'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
get :index
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :index, params: { account_id: account.id, limit: 2 }
|
||||
get :index
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
context 'when there is no ip block' do
|
||||
it 'returns an empty body' do
|
||||
get :index
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are ip blocks' do
|
||||
let!(:ip_blocks) do
|
||||
[
|
||||
IpBlock.create(ip: '192.0.2.0/24', severity: :no_access),
|
||||
IpBlock.create(ip: '172.16.0.1', severity: :sign_up_requires_approval, comment: 'Spam'),
|
||||
IpBlock.create(ip: '2001:0db8::/32', severity: :sign_up_block, expires_in: 10.days),
|
||||
]
|
||||
end
|
||||
let(:expected_response) do
|
||||
ip_blocks.map do |ip_block|
|
||||
{
|
||||
id: ip_block.id.to_s,
|
||||
ip: ip_block.ip,
|
||||
severity: ip_block.severity.to_s,
|
||||
comment: ip_block.comment,
|
||||
created_at: ip_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
|
||||
expires_at: ip_block.expires_at&.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns the correct blocked ips' do
|
||||
get :index
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json).to match_array(expected_response)
|
||||
end
|
||||
|
||||
context 'with limit param' do
|
||||
let(:params) { { limit: 2 } }
|
||||
|
||||
it 'returns only the requested number of ip blocks' do
|
||||
get :index, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json.size).to eq(params[:limit])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
let!(:ip_block) { IpBlock.create(ip: '192.0.2.0/24', severity: :no_access) }
|
||||
let(:params) { { id: ip_block.id } }
|
||||
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
get :show, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'admin:write:ip_blocks'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
get :show, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :show, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns the correct ip block' do
|
||||
get :show, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:ip]).to eq("#{ip_block.ip}/#{ip_block.ip.prefix}")
|
||||
expect(json[:severity]).to eq(ip_block.severity.to_s)
|
||||
end
|
||||
|
||||
context 'when ip block does not exist' do
|
||||
it 'returns http not found' do
|
||||
get :show, params: { id: 0 }
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:params) { { ip: '151.0.32.55', severity: 'no_access', comment: 'Spam' } }
|
||||
|
||||
context 'with wrong scope' do
|
||||
before do
|
||||
post :create, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'admin:read:ip_blocks'
|
||||
end
|
||||
|
||||
context 'with wrong role' do
|
||||
before do
|
||||
post :create, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns the correct ip block' do
|
||||
post :create, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:ip]).to eq("#{params[:ip]}/32")
|
||||
expect(json[:severity]).to eq(params[:severity])
|
||||
expect(json[:comment]).to eq(params[:comment])
|
||||
end
|
||||
|
||||
context 'when ip is not provided' do
|
||||
let(:params) { { ip: '', severity: 'no_access' } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when severity is not provided' do
|
||||
let(:params) { { ip: '173.65.23.1', severity: '' } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provided ip is already blocked' do
|
||||
before do
|
||||
IpBlock.create(params)
|
||||
end
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provided ip address is invalid' do
|
||||
let(:params) { { ip: '520.13.54.120', severity: 'no_access' } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
post :create, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
context 'when ip block exists' do
|
||||
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access', comment: 'Spam', expires_in: 48.hours) }
|
||||
let(:params) { { id: ip_block.id, severity: 'sign_up_requires_approval', comment: 'Decreasing severity' } }
|
||||
|
||||
it 'returns http success' do
|
||||
put :update, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns the correct ip block' do
|
||||
put :update, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json).to match(hash_including({
|
||||
ip: "#{ip_block.ip}/#{ip_block.ip.prefix}",
|
||||
severity: 'sign_up_requires_approval',
|
||||
comment: 'Decreasing severity',
|
||||
}))
|
||||
end
|
||||
|
||||
it 'updates the severity correctly' do
|
||||
expect { put :update, params: params }.to change { ip_block.reload.severity }.from('no_access').to('sign_up_requires_approval')
|
||||
end
|
||||
|
||||
it 'updates the comment correctly' do
|
||||
expect { put :update, params: params }.to change { ip_block.reload.comment }.from('Spam').to('Decreasing severity')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when ip block does not exist' do
|
||||
it 'returns http not found' do
|
||||
put :update, params: { id: 0 }
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
context 'when ip block exists' do
|
||||
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access') }
|
||||
let(:params) { { id: ip_block.id } }
|
||||
|
||||
it 'returns http success' do
|
||||
delete :destroy, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns an empty body' do
|
||||
delete :destroy, params: params
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json).to be_empty
|
||||
end
|
||||
|
||||
it 'deletes the ip block' do
|
||||
delete :destroy, params: params
|
||||
|
||||
expect(IpBlock.find_by(id: ip_block.id)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when ip block does not exist' do
|
||||
it 'returns http not found' do
|
||||
delete :destroy, params: { id: 0 }
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -719,65 +719,180 @@ describe StatusesController do
|
|||
end
|
||||
|
||||
context 'when status is public' do
|
||||
pending
|
||||
before do
|
||||
status.update(visibility: :public)
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is private' do
|
||||
pending
|
||||
before do
|
||||
status.update(visibility: :private)
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http not_found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is direct' do
|
||||
pending
|
||||
before do
|
||||
status.update(visibility: :direct)
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http not_found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed-in' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when status is public' do
|
||||
pending
|
||||
before do
|
||||
status.update(visibility: :public)
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is private' do
|
||||
before do
|
||||
status.update(visibility: :private)
|
||||
end
|
||||
|
||||
context 'when user is authorized to see it' do
|
||||
pending
|
||||
before do
|
||||
user.account.follow!(account)
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not authorized to see it' do
|
||||
pending
|
||||
before do
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http not_found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is direct' do
|
||||
before do
|
||||
status.update(visibility: :direct)
|
||||
end
|
||||
|
||||
context 'when user is authorized to see it' do
|
||||
pending
|
||||
before do
|
||||
Fabricate(:mention, account: user.account, status: status)
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not authorized to see it' do
|
||||
pending
|
||||
before do
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http not_found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with signature' do
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
end
|
||||
|
||||
context 'when status is public' do
|
||||
pending
|
||||
before do
|
||||
status.update(visibility: :public)
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is private' do
|
||||
before do
|
||||
status.update(visibility: :private)
|
||||
end
|
||||
|
||||
context 'when user is authorized to see it' do
|
||||
pending
|
||||
before do
|
||||
remote_account.follow!(account)
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not authorized to see it' do
|
||||
pending
|
||||
before do
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http not_found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is direct' do
|
||||
before do
|
||||
status.update(visibility: :direct)
|
||||
end
|
||||
|
||||
context 'when user is authorized to see it' do
|
||||
pending
|
||||
before do
|
||||
Fabricate(:mention, account: remote_account, status: status)
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not authorized to see it' do
|
||||
pending
|
||||
before do
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http not_found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:canonical_email_block) do
|
||||
email 'test@example.com'
|
||||
email { sequence(:email) { |i| "#{i}#{Faker::Internet.email}" } }
|
||||
reference_account { Fabricate(:account) }
|
||||
end
|
||||
|
|
35
spec/features/captcha_spec.rb
Normal file
35
spec/features/captcha_spec.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'email confirmation flow when captcha is enabled' do
|
||||
let(:user) { Fabricate(:user, confirmed_at: nil, confirmation_token: 'foobar', created_by_application: client_app) }
|
||||
let(:client_app) { nil }
|
||||
|
||||
before do
|
||||
# rubocop:disable RSpec/AnyInstance -- easiest way to deal with that that I know of
|
||||
allow_any_instance_of(Auth::ConfirmationsController).to receive(:captcha_enabled?).and_return(true)
|
||||
allow_any_instance_of(Auth::ConfirmationsController).to receive(:check_captcha!).and_return(true)
|
||||
allow_any_instance_of(Auth::ConfirmationsController).to receive(:render_captcha).and_return(nil)
|
||||
# rubocop:enable RSpec/AnyInstance
|
||||
end
|
||||
|
||||
context 'when the user signed up through an app' do
|
||||
let(:client_app) { Fabricate(:application) }
|
||||
|
||||
it 'logs in' do
|
||||
visit "/auth/confirmation?confirmation_token=#{user.confirmation_token}&redirect_to_app=true"
|
||||
|
||||
# It presents the user with a captcha form
|
||||
expect(page).to have_title(I18n.t('auth.captcha_confirmation.title'))
|
||||
|
||||
# It does not confirm the user just yet
|
||||
expect(user.reload.confirmed?).to be false
|
||||
|
||||
# It redirects to app and confirms user
|
||||
click_on I18n.t('challenge.confirm')
|
||||
expect(user.reload.confirmed?).to be true
|
||||
expect(page).to have_current_path(/\A#{client_app.confirmation_redirect_uri}/, url: true)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -39,6 +39,37 @@ RSpec.describe ActivityPub::Activity::Flag do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the report comment is excessively long' do
|
||||
subject do
|
||||
described_class.new({
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: flag_id,
|
||||
type: 'Flag',
|
||||
content: long_comment,
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: [
|
||||
ActivityPub::TagManager.instance.uri_for(flagged),
|
||||
ActivityPub::TagManager.instance.uri_for(status),
|
||||
],
|
||||
}.with_indifferent_access, sender)
|
||||
end
|
||||
|
||||
let(:long_comment) { Faker::Lorem.characters(number: 6000) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates a report but with a truncated comment' do
|
||||
report = Report.find_by(account: sender, target_account: flagged)
|
||||
|
||||
expect(report).to_not be_nil
|
||||
expect(report.comment.length).to eq 5000
|
||||
expect(report.comment).to eq long_comment[0...5000]
|
||||
expect(report.status_ids).to eq [status.id]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the reported status is private and should not be visible to the remote server' do
|
||||
let(:status) { Fabricate(:status, account: flagged, uri: 'foobar', visibility: :private) }
|
||||
|
||||
|
|
|
@ -121,10 +121,17 @@ describe Report do
|
|||
end
|
||||
|
||||
describe 'validations' do
|
||||
it 'is invalid if comment is longer than 1000 characters' do
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') }
|
||||
|
||||
it 'is invalid if comment is longer than 1000 characters only if reporter is local' do
|
||||
report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001))
|
||||
report.valid?
|
||||
expect(report.valid?).to be false
|
||||
expect(report).to model_have_error_on_field(:comment)
|
||||
end
|
||||
|
||||
it 'is valid if comment is longer than 1000 characters and reporter is not local' do
|
||||
report = Fabricate.build(:report, account: remote_account, comment: Faker::Lorem.characters(number: 1001))
|
||||
expect(report.valid?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,14 @@ RSpec.describe ReportService, type: :service do
|
|||
subject { described_class.new }
|
||||
|
||||
let(:source_account) { Fabricate(:account) }
|
||||
let(:target_account) { Fabricate(:account) }
|
||||
|
||||
context 'with a local account' do
|
||||
it 'has a uri' do
|
||||
report = subject.call(source_account, target_account)
|
||||
expect(report.uri).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a remote account' do
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') }
|
||||
|
@ -35,7 +43,6 @@ RSpec.describe ReportService, type: :service do
|
|||
-> { described_class.new.call(source_account, target_account, status_ids: [status.id]) }
|
||||
end
|
||||
|
||||
let(:target_account) { Fabricate(:account) }
|
||||
let(:status) { Fabricate(:status, account: target_account, visibility: :direct) }
|
||||
|
||||
context 'when it is addressed to the reporter' do
|
||||
|
@ -91,8 +98,7 @@ RSpec.describe ReportService, type: :service do
|
|||
-> { described_class.new.call(source_account, target_account) }
|
||||
end
|
||||
|
||||
let!(:target_account) { Fabricate(:account) }
|
||||
let!(:other_report) { Fabricate(:report, target_account: target_account) }
|
||||
let!(:other_report) { Fabricate(:report, target_account: target_account) }
|
||||
|
||||
before do
|
||||
ActionMailer::Base.deliveries.clear
|
||||
|
|
185
yarn.lock
185
yarn.lock
|
@ -1055,14 +1055,6 @@
|
|||
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||
|
||||
"@babel/runtime-corejs3@^7.10.2":
|
||||
version "7.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.10.3.tgz#931ed6941d3954924a7aa967ee440e60c507b91a"
|
||||
integrity sha512-HA7RPj5xvJxQl429r5Cxr2trJwOfPjKiqhCXcdQPSqO2G0RHPZpXu4fkYmBaTKCp2c/jRaMK9GB/lN+7zvvFPw==
|
||||
dependencies:
|
||||
core-js-pure "^3.0.0"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c"
|
||||
|
@ -1070,7 +1062,7 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.12.0"
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
||||
version "7.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200"
|
||||
integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==
|
||||
|
@ -1822,18 +1814,18 @@
|
|||
magic-string "^0.25.0"
|
||||
string.prototype.matchall "^4.0.6"
|
||||
|
||||
"@testing-library/dom@^8.0.0":
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.1.0.tgz#f8358b1883844ea569ba76b7e94582168df5370d"
|
||||
integrity sha512-kmW9alndr19qd6DABzQ978zKQ+J65gU2Rzkl8hriIetPnwpesRaK4//jEQyYh8fEALmGhomD/LBQqt+o+DL95Q==
|
||||
"@testing-library/dom@^9.0.0":
|
||||
version "9.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.2.0.tgz#0e1f45e956f2a16f471559c06edd8827c4832f04"
|
||||
integrity sha512-xTEnpUKiV/bMyEsE5bT4oYA0x0Z/colMtxzUY8bKyPXBNLn/e0V4ZjBZkEhms0xE4pv9QsPfSRu9AWS4y5wGvA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.10.4"
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@types/aria-query" "^4.2.0"
|
||||
aria-query "^4.2.2"
|
||||
"@types/aria-query" "^5.0.1"
|
||||
aria-query "^5.0.0"
|
||||
chalk "^4.1.0"
|
||||
dom-accessibility-api "^0.5.6"
|
||||
lz-string "^1.4.4"
|
||||
dom-accessibility-api "^0.5.9"
|
||||
lz-string "^1.5.0"
|
||||
pretty-format "^27.0.2"
|
||||
|
||||
"@testing-library/jest-dom@^5.16.5":
|
||||
|
@ -1851,14 +1843,14 @@
|
|||
lodash "^4.17.15"
|
||||
redent "^3.0.0"
|
||||
|
||||
"@testing-library/react@^12.1.5":
|
||||
version "12.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b"
|
||||
integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==
|
||||
"@testing-library/react@^14.0.0":
|
||||
version "14.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.0.0.tgz#59030392a6792450b9ab8e67aea5f3cc18d6347c"
|
||||
integrity sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@testing-library/dom" "^8.0.0"
|
||||
"@types/react-dom" "<18.0.0"
|
||||
"@testing-library/dom" "^9.0.0"
|
||||
"@types/react-dom" "^18.0.0"
|
||||
|
||||
"@tootallnate/once@2":
|
||||
version "2.0.0"
|
||||
|
@ -1870,10 +1862,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
|
||||
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
||||
|
||||
"@types/aria-query@^4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0"
|
||||
integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==
|
||||
"@types/aria-query@^5.0.1":
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc"
|
||||
integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==
|
||||
|
||||
"@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.3":
|
||||
version "7.1.18"
|
||||
|
@ -2184,29 +2176,17 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
|
||||
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
|
||||
|
||||
"@types/raf@^3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.0.tgz#2b72cbd55405e071f1c4d29992638e022b20acc2"
|
||||
integrity sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==
|
||||
|
||||
"@types/range-parser@*":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
|
||||
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
|
||||
|
||||
"@types/react-dom@<18.0.0":
|
||||
version "17.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.15.tgz#f2c8efde11521a4b7991e076cb9c70ba3bb0d156"
|
||||
integrity sha512-Tr9VU9DvNoHDWlmecmcsE5ZZiUkYx+nKBzum4Oxe1K0yJVyBlfbq7H3eXjxXqJczBKqPGq3EgfTru4MgKb9+Yw==
|
||||
"@types/react-dom@^18.0.0", "@types/react-dom@^18.2.4":
|
||||
version "18.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.4.tgz#13f25bfbf4e404d26f62ac6e406591451acba9e0"
|
||||
integrity sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==
|
||||
dependencies:
|
||||
"@types/react" "^17"
|
||||
|
||||
"@types/react-dom@^16.9.18":
|
||||
version "16.9.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.18.tgz#1fda8b84370b1339d639a797a84c16d5a195b419"
|
||||
integrity sha512-lmNARUX3+rNF/nmoAFqasG0jAA7q6MeGZK/fdeLwY3kAA4NPgHHrG5bNQe2B5xmD4B+x6Z6h0rEJQ7MEEgQxsw==
|
||||
dependencies:
|
||||
"@types/react" "^16"
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-helmet@^6.1.6":
|
||||
version "6.1.6"
|
||||
|
@ -2328,28 +2308,10 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^17":
|
||||
version "17.0.44"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.44.tgz#c3714bd34dd551ab20b8015d9d0dbec812a51ec7"
|
||||
integrity sha512-Ye0nlw09GeMp2Suh8qoOv0odfgCoowfM/9MG6WeRD60Gq9wS90bdkdRtYbRkNhXOpG4H+YXGvj4wOWhAC0LJ1g==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/react@>=16.9.11":
|
||||
version "18.0.26"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917"
|
||||
integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/react@^16", "@types/react@^16.14.38":
|
||||
version "16.14.38"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.38.tgz#b814d157ca8906603593d5106f6d733af9b79df4"
|
||||
integrity sha512-PbEjuhwkdH6IB5Sak6BFAqpVMHY/wJxa0EG3bKkr0vWA2hSDIq3iEMhHyqjXrDFMqRzkiQkdyNXOnoELrh/9aQ==
|
||||
"@types/react@*", "@types/react@>=16.9.11", "@types/react@^18.0.26":
|
||||
version "18.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.6.tgz#5cd53ee0d30ffc193b159d3516c8c8ad2f19d571"
|
||||
integrity sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
|
@ -2980,14 +2942,6 @@ argparse@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||
|
||||
aria-query@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
|
||||
integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.2"
|
||||
"@babel/runtime-corejs3" "^7.10.2"
|
||||
|
||||
aria-query@^5.0.0, aria-query@^5.1.3:
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
|
||||
|
@ -4156,11 +4110,6 @@ core-js-compat@^3.25.1:
|
|||
dependencies:
|
||||
browserslist "^4.21.4"
|
||||
|
||||
core-js-pure@^3.0.0:
|
||||
version "3.6.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
||||
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
||||
|
||||
core-js@^2.5.0:
|
||||
version "2.6.12"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||
|
@ -4761,6 +4710,11 @@ dom-accessibility-api@^0.5.6:
|
|||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9"
|
||||
integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw==
|
||||
|
||||
dom-accessibility-api@^0.5.9:
|
||||
version "0.5.16"
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
|
||||
integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
|
||||
|
||||
dom-helpers@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
|
||||
|
@ -7908,10 +7862,10 @@ lru-cache@^9.0.0:
|
|||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.0.1.tgz#ac061ed291f8b9adaca2b085534bb1d3b61bef83"
|
||||
integrity sha512-C8QsKIN1UIXeOs3iWmiZ1lQY+EnKDojWd37fXy1aSbJvH4iSma1uy2OWuoB3m4SYRli5+CUjDv3Dij5DVoetmg==
|
||||
|
||||
lz-string@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||
integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
|
||||
lz-string@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
|
||||
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
|
||||
|
||||
magic-string@^0.25.0, magic-string@^0.25.7:
|
||||
version "0.25.9"
|
||||
|
@ -9502,7 +9456,7 @@ quick-lru@^4.0.1:
|
|||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
|
||||
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
|
||||
|
||||
raf@^3.1.0, raf@^3.4.1:
|
||||
raf@^3.1.0:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
|
||||
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
|
||||
|
@ -9539,15 +9493,13 @@ raw-body@2.5.1:
|
|||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
react-dom@^16.14.0:
|
||||
version "16.14.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
|
||||
integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
|
||||
react-dom@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.19.1"
|
||||
scheduler "^0.23.0"
|
||||
|
||||
react-event-listener@^0.6.0:
|
||||
version "0.6.6"
|
||||
|
@ -9617,7 +9569,12 @@ react-intl@^2.9.0:
|
|||
intl-relativeformat "^2.1.0"
|
||||
invariant "^2.1.1"
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.6:
|
||||
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
@ -9735,6 +9692,14 @@ react-select@*, react-select@^5.7.3:
|
|||
react-transition-group "^4.3.0"
|
||||
use-isomorphic-layout-effect "^1.1.2"
|
||||
|
||||
react-shallow-renderer@^16.15.0:
|
||||
version "16.15.0"
|
||||
resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457"
|
||||
integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
|
||||
|
||||
react-side-effect@^2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a"
|
||||
|
@ -9778,15 +9743,14 @@ react-swipeable-views@^0.14.0:
|
|||
react-swipeable-views-utils "^0.14.0"
|
||||
warning "^4.0.1"
|
||||
|
||||
react-test-renderer@^16.14.0:
|
||||
version "16.14.0"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae"
|
||||
integrity sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==
|
||||
react-test-renderer@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
|
||||
integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
react-is "^16.8.6"
|
||||
scheduler "^0.19.1"
|
||||
react-is "^18.2.0"
|
||||
react-shallow-renderer "^16.15.0"
|
||||
scheduler "^0.23.0"
|
||||
|
||||
react-textarea-autosize@*, react-textarea-autosize@^8.4.1:
|
||||
version "8.4.1"
|
||||
|
@ -9814,14 +9778,12 @@ react-transition-group@^4.3.0:
|
|||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react@^16.14.0:
|
||||
version "16.14.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
|
||||
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
|
||||
react@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
read-pkg-up@^7.0.1:
|
||||
version "7.0.1"
|
||||
|
@ -9951,7 +9913,7 @@ regenerator-runtime@^0.12.0:
|
|||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
|
||||
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
|
||||
|
||||
regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4:
|
||||
regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.3:
|
||||
version "0.13.11"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
|
||||
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
|
||||
|
@ -10301,13 +10263,12 @@ saxes@^6.0.0:
|
|||
dependencies:
|
||||
xmlchars "^2.2.0"
|
||||
|
||||
scheduler@^0.19.1:
|
||||
version "0.19.1"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
|
||||
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
|
||||
scheduler@^0.23.0:
|
||||
version "0.23.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
||||
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
schema-utils@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue