Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
04d825fdc0
|
@ -24,7 +24,7 @@ test:
|
|||
- apt install firefox-esr -y --no-install-recommends
|
||||
- firefox --version
|
||||
- yarn
|
||||
- npm run unit
|
||||
- yarn unit
|
||||
|
||||
build:
|
||||
stage: build
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
|
||||
|
||||
const NavPanel = {
|
||||
created () {
|
||||
if (this.currentUser && this.currentUser.locked) {
|
||||
const store = this.$store
|
||||
const credentials = store.state.users.currentUser.credentials
|
||||
|
||||
followRequestFetcher.startFetching({ store, credentials })
|
||||
this.$store.dispatch('startFetchingFollowRequest')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -270,6 +270,17 @@
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.fun') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<Checkbox v-model="greentext">
|
||||
{{ $t('settings.greentext') }} {{ $t('settings.instance_default', { value: greentextLocalizedValue }) }}
|
||||
</Checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :label="$t('settings.theme')">
|
||||
|
|
|
@ -10,6 +10,10 @@ const SideDrawer = {
|
|||
}),
|
||||
created () {
|
||||
this.closeGesture = GestureService.swipeGesture(GestureService.DIRECTION_LEFT, this.toggleDrawer)
|
||||
|
||||
if (this.currentUser && this.currentUser.locked) {
|
||||
this.$store.dispatch('startFetchingFollowRequest')
|
||||
}
|
||||
},
|
||||
components: { UserCard },
|
||||
computed: {
|
||||
|
|
|
@ -13,10 +13,11 @@ import Timeago from '../timeago/timeago.vue'
|
|||
import StatusPopover from '../status_popover/status_popover.vue'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
import fileType from 'src/services/file_type/file_type.service'
|
||||
import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js'
|
||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
|
||||
import { filter, unescape, uniqBy } from 'lodash'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
|
||||
const Status = {
|
||||
name: 'Status',
|
||||
|
@ -42,8 +43,8 @@ const Status = {
|
|||
showingTall: this.inConversation && this.focused,
|
||||
showingLongSubject: false,
|
||||
error: null,
|
||||
expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject,
|
||||
betterShadow: this.$store.state.interface.browserSupport.cssFilter
|
||||
// not as computed because it sets the initial state which will be changed later
|
||||
expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -103,7 +104,7 @@ const Status = {
|
|||
return this.$store.state.statuses.allStatusesObject[this.status.id]
|
||||
},
|
||||
loggedIn () {
|
||||
return !!this.$store.state.users.currentUser
|
||||
return !!this.currentUser
|
||||
},
|
||||
muteWordHits () {
|
||||
const statusText = this.status.text.toLowerCase()
|
||||
|
@ -163,7 +164,7 @@ const Status = {
|
|||
if (this.inConversation || !this.isReply) {
|
||||
return false
|
||||
}
|
||||
if (this.status.user.id === this.$store.state.users.currentUser.id) {
|
||||
if (this.status.user.id === this.currentUser.id) {
|
||||
return false
|
||||
}
|
||||
if (this.status.type === 'retweet') {
|
||||
|
@ -178,7 +179,7 @@ const Status = {
|
|||
if (checkFollowing && taggedUser && taggedUser.following) {
|
||||
return false
|
||||
}
|
||||
if (this.status.attentions[i].id === this.$store.state.users.currentUser.id) {
|
||||
if (this.status.attentions[i].id === this.currentUser.id) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -255,11 +256,41 @@ const Status = {
|
|||
maxThumbnails () {
|
||||
return this.mergedConfig.maxThumbnails
|
||||
},
|
||||
postBodyHtml () {
|
||||
const html = this.status.statusnet_html
|
||||
|
||||
if (this.mergedConfig.greentext) {
|
||||
try {
|
||||
if (html.includes('>')) {
|
||||
// This checks if post has '>' at the beginning, excluding mentions so that @mention >impying works
|
||||
return processHtml(html, (string) => {
|
||||
if (string.includes('>') &&
|
||||
string
|
||||
.replace(/<[^>]+?>/gi, '') // remove all tags
|
||||
.replace(/@\w+/gi, '') // remove mentions (even failed ones)
|
||||
.trim()
|
||||
.startsWith('>')) {
|
||||
return `<span class='greentext'>${string}</span>`
|
||||
} else {
|
||||
return string
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return html
|
||||
}
|
||||
} catch (e) {
|
||||
console.err('Failed to process status html', e)
|
||||
return html
|
||||
}
|
||||
} else {
|
||||
return html
|
||||
}
|
||||
},
|
||||
contentHtml () {
|
||||
if (!this.status.summary_html) {
|
||||
return this.status.statusnet_html
|
||||
return this.postBodyHtml
|
||||
}
|
||||
return this.status.summary_html + '<br />' + this.status.statusnet_html
|
||||
return this.status.summary_html + '<br />' + this.postBodyHtml
|
||||
},
|
||||
combinedFavsAndRepeatsUsers () {
|
||||
// Use the status from the global status repository since favs and repeats are saved in it
|
||||
|
@ -270,7 +301,7 @@ const Status = {
|
|||
return uniqBy(combinedUsers, 'id')
|
||||
},
|
||||
ownStatus () {
|
||||
return this.status.user.id === this.$store.state.users.currentUser.id
|
||||
return this.status.user.id === this.currentUser.id
|
||||
},
|
||||
tags () {
|
||||
return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ')
|
||||
|
@ -278,7 +309,11 @@ const Status = {
|
|||
hidePostStats () {
|
||||
return this.mergedConfig.hidePostStats
|
||||
},
|
||||
...mapGetters(['mergedConfig'])
|
||||
...mapGetters(['mergedConfig']),
|
||||
...mapState({
|
||||
betterShadow: state => state.interface.browserSupport.cssFilter,
|
||||
currentUser: state => state.users.currentUser
|
||||
})
|
||||
},
|
||||
components: {
|
||||
Attachment,
|
||||
|
|
|
@ -606,7 +606,7 @@ $status-margin: 0.75em;
|
|||
height: 100%;
|
||||
mask: linear-gradient(to top, white, transparent) bottom/100% 70px no-repeat,
|
||||
linear-gradient(to top, white, white);
|
||||
// Autoprefixed seem to ignore this one, and also syntax is different
|
||||
/* Autoprefixed seem to ignore this one, and also syntax is different */
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
}
|
||||
|
@ -752,7 +752,8 @@ $status-margin: 0.75em;
|
|||
}
|
||||
|
||||
.greentext {
|
||||
color: green;
|
||||
color: $fallback--cGreen;
|
||||
color: var(--cGreen, $fallback--cGreen);
|
||||
}
|
||||
|
||||
.status-conversation {
|
||||
|
|
|
@ -370,6 +370,8 @@
|
|||
"false": "no",
|
||||
"true": "yes"
|
||||
},
|
||||
"fun": "Fun",
|
||||
"greentext": "Meme arrows",
|
||||
"notifications": "Notifications",
|
||||
"notification_setting": "Receive notifications from:",
|
||||
"notification_setting_follows": "Users you follow",
|
||||
|
|
|
@ -23,7 +23,7 @@ const messages = {
|
|||
he: require('./he.json'),
|
||||
hu: require('./hu.json'),
|
||||
it: require('./it.json'),
|
||||
ja: require('./ja.json'),
|
||||
ja: require('./ja_pedantic.json'),
|
||||
ja_easy: require('./ja_easy.json'),
|
||||
ko: require('./ko.json'),
|
||||
nb: require('./nb.json'),
|
||||
|
|
|
@ -174,6 +174,8 @@
|
|||
"name_bio": "Имя и описание",
|
||||
"new_email": "Новый email",
|
||||
"new_password": "Новый пароль",
|
||||
"fun": "Потешное",
|
||||
"greentext": "Мемные стрелочки",
|
||||
"notification_visibility": "Показывать уведомления",
|
||||
"notification_visibility_follows": "Подписки",
|
||||
"notification_visibility_likes": "Лайки",
|
||||
|
|
|
@ -43,6 +43,13 @@ const api = {
|
|||
const fetcher = store.state.backendInteractor.startFetchingNotifications({ store })
|
||||
store.commit('addFetcher', { fetcherName: 'notifications', fetcher })
|
||||
},
|
||||
startFetchingFollowRequest (store) {
|
||||
// Don't start fetching if we already are.
|
||||
if (store.state.fetchers['followRequest']) return
|
||||
|
||||
const fetcher = store.state.backendInteractor.startFetchingFollowRequest({ store })
|
||||
store.commit('addFetcher', { fetcherName: 'followRequest', fetcher })
|
||||
},
|
||||
stopFetching (store, fetcherName) {
|
||||
const fetcher = store.state.fetchers[fetcherName]
|
||||
window.clearInterval(fetcher)
|
||||
|
|
|
@ -45,6 +45,7 @@ export const defaultState = {
|
|||
playVideosInModal: false,
|
||||
useOneClickNsfw: false,
|
||||
useContainFit: false,
|
||||
greentext: undefined, // instance default
|
||||
hidePostStats: undefined, // instance default
|
||||
hideUserStats: undefined // instance default
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ const defaultState = {
|
|||
noAttachmentLinks: false,
|
||||
showFeaturesPanel: true,
|
||||
minimalScopesMode: false,
|
||||
greentext: false,
|
||||
|
||||
// Nasty stuff
|
||||
pleromaBackend: true,
|
||||
|
|
|
@ -434,6 +434,7 @@ const users = {
|
|||
store.dispatch('stopFetching', 'friends')
|
||||
store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))
|
||||
store.dispatch('stopFetching', 'notifications')
|
||||
store.dispatch('stopFetching', 'followRequest')
|
||||
store.commit('clearNotifications')
|
||||
store.commit('resetStatuses')
|
||||
})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import apiService from '../api/api.service.js'
|
||||
import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js'
|
||||
import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'
|
||||
import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
|
||||
|
||||
const backendInteractorService = credentials => {
|
||||
const fetchStatus = ({ id }) => {
|
||||
|
@ -63,6 +64,10 @@ const backendInteractorService = credentials => {
|
|||
return notificationsFetcher.startFetching({ store, credentials })
|
||||
}
|
||||
|
||||
const startFetchingFollowRequest = ({ store }) => {
|
||||
return followRequestFetcher.startFetching({ store, credentials })
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
const tagUser = ({ screen_name }, tag) => {
|
||||
return apiService.tagUser({ screen_name, tag, credentials })
|
||||
|
@ -111,7 +116,6 @@ const backendInteractorService = credentials => {
|
|||
const subscribeUser = (id) => apiService.subscribeUser({ credentials, id })
|
||||
const unsubscribeUser = (id) => apiService.unsubscribeUser({ credentials, id })
|
||||
const fetchBlocks = () => apiService.fetchBlocks({ credentials })
|
||||
const fetchFollowRequests = () => apiService.fetchFollowRequests({ credentials })
|
||||
const fetchOAuthTokens = () => apiService.fetchOAuthTokens({ credentials })
|
||||
const revokeOAuthToken = (id) => apiService.revokeOAuthToken({ id, credentials })
|
||||
const fetchPinnedStatuses = (id) => apiService.fetchPinnedStatuses({ credentials, id })
|
||||
|
@ -168,6 +172,7 @@ const backendInteractorService = credentials => {
|
|||
verifyCredentials: apiService.verifyCredentials,
|
||||
startFetchingTimeline,
|
||||
startFetchingNotifications,
|
||||
startFetchingFollowRequest,
|
||||
fetchMutes,
|
||||
muteUser,
|
||||
unmuteUser,
|
||||
|
@ -203,7 +208,6 @@ const backendInteractorService = credentials => {
|
|||
mfaSetupOTP,
|
||||
mfaConfirmOTP,
|
||||
mfaDisableOTP,
|
||||
fetchFollowRequests,
|
||||
approveUser,
|
||||
denyUser,
|
||||
vote,
|
||||
|
|
|
@ -46,6 +46,14 @@ export const parseUser = (data) => {
|
|||
output.description = data.note
|
||||
output.description_html = addEmojis(data.note, data.emojis)
|
||||
|
||||
output.fields = data.fields
|
||||
output.fields_html = data.fields.map(field => {
|
||||
return {
|
||||
name: addEmojis(field.name, data.emojis),
|
||||
value: addEmojis(field.value, data.emojis)
|
||||
}
|
||||
})
|
||||
|
||||
// Utilize avatar_static for gif avatars?
|
||||
output.profile_image_url = data.avatar
|
||||
output.profile_image_url_original = data.avatar
|
||||
|
@ -95,6 +103,7 @@ export const parseUser = (data) => {
|
|||
if (data.source) {
|
||||
output.description = data.source.note
|
||||
output.default_scope = data.source.privacy
|
||||
output.fields = data.source.fields
|
||||
if (data.source.pleroma) {
|
||||
output.no_rich_text = data.source.pleroma.no_rich_text
|
||||
output.show_role = data.source.pleroma.show_role
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* This is a tiny purpose-built HTML parser/processor. This basically detects any type of visual newline and
|
||||
* allows it to be processed, useful for greentexting, mostly
|
||||
*
|
||||
* known issue: doesn't handle CDATA so nested CDATA might not work well
|
||||
*
|
||||
* @param {Object} input - input data
|
||||
* @param {(string) => string} processor - function that will be called on every line
|
||||
* @return {string} processed html
|
||||
*/
|
||||
export const processHtml = (html, processor) => {
|
||||
const handledTags = new Set(['p', 'br', 'div'])
|
||||
const openCloseTags = new Set(['p', 'div'])
|
||||
|
||||
let buffer = '' // Current output buffer
|
||||
const level = [] // How deep we are in tags and which tags were there
|
||||
let textBuffer = '' // Current line content
|
||||
let tagBuffer = null // Current tag buffer, if null = we are not currently reading a tag
|
||||
|
||||
// Extracts tag name from tag, i.e. <span a="b"> => span
|
||||
const getTagName = (tag) => {
|
||||
const result = /(?:<\/(\w+)>|<(\w+)\s?[^/]*?\/?>)/gi.exec(tag)
|
||||
return result && (result[1] || result[2])
|
||||
}
|
||||
|
||||
const flush = () => { // Processes current line buffer, adds it to output buffer and clears line buffer
|
||||
if (textBuffer.trim().length > 0) {
|
||||
buffer += processor(textBuffer)
|
||||
} else {
|
||||
buffer += textBuffer
|
||||
}
|
||||
textBuffer = ''
|
||||
}
|
||||
|
||||
const handleBr = (tag) => { // handles single newlines/linebreaks/selfclosing
|
||||
flush()
|
||||
buffer += tag
|
||||
}
|
||||
|
||||
const handleOpen = (tag) => { // handles opening tags
|
||||
flush()
|
||||
buffer += tag
|
||||
level.push(tag)
|
||||
}
|
||||
|
||||
const handleClose = (tag) => { // handles closing tags
|
||||
flush()
|
||||
buffer += tag
|
||||
if (level[level.length - 1] === tag) {
|
||||
level.pop()
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < html.length; i++) {
|
||||
const char = html[i]
|
||||
if (char === '<' && tagBuffer === null) {
|
||||
tagBuffer = char
|
||||
} else if (char !== '>' && tagBuffer !== null) {
|
||||
tagBuffer += char
|
||||
} else if (char === '>' && tagBuffer !== null) {
|
||||
tagBuffer += char
|
||||
const tagFull = tagBuffer
|
||||
tagBuffer = null
|
||||
const tagName = getTagName(tagFull)
|
||||
if (handledTags.has(tagName)) {
|
||||
if (tagName === 'br') {
|
||||
handleBr(tagFull)
|
||||
} else if (openCloseTags.has(tagName)) {
|
||||
if (tagFull[1] === '/') {
|
||||
handleClose(tagFull)
|
||||
} else if (tagFull[tagFull.length - 2] === '/') {
|
||||
// self-closing
|
||||
handleBr(tagFull)
|
||||
} else {
|
||||
handleOpen(tagFull)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
textBuffer += tagFull
|
||||
}
|
||||
} else if (char === '\n') {
|
||||
handleBr(char)
|
||||
} else {
|
||||
textBuffer += char
|
||||
}
|
||||
}
|
||||
if (tagBuffer) {
|
||||
textBuffer += tagBuffer
|
||||
}
|
||||
|
||||
flush()
|
||||
|
||||
return buffer
|
||||
}
|
|
@ -277,6 +277,19 @@ describe('API Entities normalizer', () => {
|
|||
expect(parsedUser).to.have.property('description_html').that.contains('<img')
|
||||
})
|
||||
|
||||
it('adds emojis to user profile fields', () => {
|
||||
const user = makeMockUserMasto({ emojis: makeMockEmojiMasto(), fields: [{ name: ':thinking:', value: ':image:' }] })
|
||||
|
||||
const parsedUser = parseUser(user)
|
||||
|
||||
expect(parsedUser).to.have.property('fields_html').to.be.an('array')
|
||||
|
||||
const field = parsedUser.fields_html[0]
|
||||
|
||||
expect(field).to.have.property('name').that.contains('<img')
|
||||
expect(field).to.have.property('value').that.contains('<img')
|
||||
})
|
||||
|
||||
it('adds hide_follows and hide_followers user settings', () => {
|
||||
const user = makeMockUserMasto({ pleroma: { hide_followers: true, hide_follows: false, hide_followers_count: false, hide_follows_count: true } })
|
||||
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js'
|
||||
|
||||
describe('TinyPostHTMLProcessor', () => {
|
||||
describe('with processor that keeps original line should not make any changes to HTML when', () => {
|
||||
const processorKeep = (line) => line
|
||||
it('fed with regular HTML with newlines', () => {
|
||||
const inputOutput = '1<br/>2<p class="lol">3 4</p> 5 \n 6 <p > 7 <br> 8 </p> <br>\n<br/>'
|
||||
expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput)
|
||||
})
|
||||
|
||||
it('fed with possibly broken HTML with invalid tags/composition', () => {
|
||||
const inputOutput = '<feeee dwdwddddddw> <i>ayy<b>lm</i>ao</b> </section>'
|
||||
expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput)
|
||||
})
|
||||
|
||||
it('fed with very broken HTML with broken composition', () => {
|
||||
const inputOutput = '</p> lmao what </div> whats going on <div> wha <p>'
|
||||
expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput)
|
||||
})
|
||||
|
||||
it('fed with sorta valid HTML but tags aren\'t closed', () => {
|
||||
const inputOutput = 'just leaving a <div> hanging'
|
||||
expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput)
|
||||
})
|
||||
|
||||
it('fed with not really HTML at this point... tags that aren\'t finished', () => {
|
||||
const inputOutput = 'do you expect me to finish this <div class='
|
||||
expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput)
|
||||
})
|
||||
|
||||
it('fed with dubiously valid HTML (p within p and also div inside p)', () => {
|
||||
const inputOutput = 'look ma <p> p \nwithin <p> p! </p> and a <br/><div>div!</div></p>'
|
||||
expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput)
|
||||
})
|
||||
|
||||
it('fed with maybe valid HTML? self-closing divs and ps', () => {
|
||||
const inputOutput = 'a <div class="what"/> what now <p aria-label="wtf"/> ?'
|
||||
expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput)
|
||||
})
|
||||
|
||||
it('fed with valid XHTML containing a CDATA', () => {
|
||||
const inputOutput = 'Yes, it is me, <![CDATA[DIO]]>'
|
||||
expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput)
|
||||
})
|
||||
})
|
||||
describe('with processor that replaces lines with word "_" should match expected line when', () => {
|
||||
const processorReplace = (line) => '_'
|
||||
it('fed with regular HTML with newlines', () => {
|
||||
const input = '1<br/>2<p class="lol">3 4</p> 5 \n 6 <p > 7 <br> 8 </p> <br>\n<br/>'
|
||||
const output = '_<br/>_<p class="lol">_</p>_\n_<p >_<br>_</p> <br>\n<br/>'
|
||||
expect(processHtml(input, processorReplace)).to.eql(output)
|
||||
})
|
||||
|
||||
it('fed with possibly broken HTML with invalid tags/composition', () => {
|
||||
const input = '<feeee dwdwddddddw> <i>ayy<b>lm</i>ao</b> </section>'
|
||||
const output = '_'
|
||||
expect(processHtml(input, processorReplace)).to.eql(output)
|
||||
})
|
||||
|
||||
it('fed with very broken HTML with broken composition', () => {
|
||||
const input = '</p> lmao what </div> whats going on <div> wha <p>'
|
||||
const output = '</p>_</div>_<div>_<p>'
|
||||
expect(processHtml(input, processorReplace)).to.eql(output)
|
||||
})
|
||||
|
||||
it('fed with sorta valid HTML but tags aren\'t closed', () => {
|
||||
const input = 'just leaving a <div> hanging'
|
||||
const output = '_<div>_'
|
||||
expect(processHtml(input, processorReplace)).to.eql(output)
|
||||
})
|
||||
|
||||
it('fed with not really HTML at this point... tags that aren\'t finished', () => {
|
||||
const input = 'do you expect me to finish this <div class='
|
||||
const output = '_'
|
||||
expect(processHtml(input, processorReplace)).to.eql(output)
|
||||
})
|
||||
|
||||
it('fed with dubiously valid HTML (p within p and also div inside p)', () => {
|
||||
const input = 'look ma <p> p \nwithin <p> p! </p> and a <br/><div>div!</div></p>'
|
||||
const output = '_<p>_\n_<p>_</p>_<br/><div>_</div></p>'
|
||||
expect(processHtml(input, processorReplace)).to.eql(output)
|
||||
})
|
||||
|
||||
it('fed with maybe valid HTML? self-closing divs and ps', () => {
|
||||
const input = 'a <div class="what"/> what now <p aria-label="wtf"/> ?'
|
||||
const output = '_<div class="what"/>_<p aria-label="wtf"/>_'
|
||||
expect(processHtml(input, processorReplace)).to.eql(output)
|
||||
})
|
||||
|
||||
it('fed with valid XHTML containing a CDATA', () => {
|
||||
const input = 'Yes, it is me, <![CDATA[DIO]]>'
|
||||
const output = '_'
|
||||
expect(processHtml(input, processorReplace)).to.eql(output)
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue