Apply prettier formatting
This commit is contained in:
@@ -63,24 +63,15 @@ describe("ContentMessages", () => {
|
||||
describe("sendStickerContentToRoom", () => {
|
||||
beforeEach(() => {
|
||||
mocked(client.sendStickerMessage).mockReturnValue(prom);
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(<T>(
|
||||
roomId: string,
|
||||
fn: (actualRoomId: string) => Promise<T>,
|
||||
client?: MatrixClient,
|
||||
) => {
|
||||
return fn(roomId);
|
||||
});
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(
|
||||
<T>(roomId: string, fn: (actualRoomId: string) => Promise<T>, client?: MatrixClient) => {
|
||||
return fn(roomId);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should forward the call to doMaybeLocalRoomAction", async () => {
|
||||
await contentMessages.sendStickerContentToRoom(
|
||||
stickerUrl,
|
||||
roomId,
|
||||
null,
|
||||
imageInfo,
|
||||
text,
|
||||
client,
|
||||
);
|
||||
await contentMessages.sendStickerContentToRoom(stickerUrl, roomId, null, imageInfo, text, client);
|
||||
expect(client.sendStickerMessage).toHaveBeenCalledWith(roomId, null, stickerUrl, imageInfo, text);
|
||||
});
|
||||
});
|
||||
@@ -88,22 +79,25 @@ describe("ContentMessages", () => {
|
||||
describe("sendContentToRoom", () => {
|
||||
const roomId = "!roomId:server";
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(global.Image.prototype, 'src', {
|
||||
Object.defineProperty(global.Image.prototype, "src", {
|
||||
// Define the property setter
|
||||
set(src) {
|
||||
window.setTimeout(() => this.onload());
|
||||
},
|
||||
});
|
||||
Object.defineProperty(global.Image.prototype, 'height', {
|
||||
get() { return 600; },
|
||||
Object.defineProperty(global.Image.prototype, "height", {
|
||||
get() {
|
||||
return 600;
|
||||
},
|
||||
});
|
||||
Object.defineProperty(global.Image.prototype, 'width', {
|
||||
get() { return 800; },
|
||||
Object.defineProperty(global.Image.prototype, "width", {
|
||||
get() {
|
||||
return 800;
|
||||
},
|
||||
});
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(<T>(
|
||||
roomId: string,
|
||||
fn: (actualRoomId: string) => Promise<T>,
|
||||
) => fn(roomId));
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(
|
||||
<T>(roomId: string, fn: (actualRoomId: string) => Promise<T>) => fn(roomId),
|
||||
);
|
||||
mocked(BlurhashEncoder.instance.getBlurhash).mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
@@ -111,34 +105,46 @@ describe("ContentMessages", () => {
|
||||
mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" });
|
||||
const file = new File([], "fileName", { type: "image/jpeg" });
|
||||
await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined);
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.image",
|
||||
}));
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
null,
|
||||
expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.image",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should fall back to m.file for invalid image files", async () => {
|
||||
mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" });
|
||||
const file = new File([], "fileName", { type: "image/png" });
|
||||
await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined);
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.file",
|
||||
}));
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
null,
|
||||
expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.file",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should use m.video for video files", async () => {
|
||||
jest.spyOn(document, "createElement").mockImplementation(tagName => {
|
||||
jest.spyOn(document, "createElement").mockImplementation((tagName) => {
|
||||
const element = createElement(tagName);
|
||||
if (tagName === "video") {
|
||||
(<HTMLVideoElement>element).load = jest.fn();
|
||||
(<HTMLVideoElement>element).play = () => element.onloadeddata(new Event("loadeddata"));
|
||||
(<HTMLVideoElement>element).pause = jest.fn();
|
||||
Object.defineProperty(element, 'videoHeight', {
|
||||
get() { return 600; },
|
||||
Object.defineProperty(element, "videoHeight", {
|
||||
get() {
|
||||
return 600;
|
||||
},
|
||||
});
|
||||
Object.defineProperty(element, 'videoWidth', {
|
||||
get() { return 800; },
|
||||
Object.defineProperty(element, "videoWidth", {
|
||||
get() {
|
||||
return 800;
|
||||
},
|
||||
});
|
||||
}
|
||||
return element;
|
||||
@@ -147,31 +153,43 @@ describe("ContentMessages", () => {
|
||||
mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" });
|
||||
const file = new File([], "fileName", { type: "video/mp4" });
|
||||
await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined);
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.video",
|
||||
}));
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
null,
|
||||
expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.video",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should use m.audio for audio files", async () => {
|
||||
mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" });
|
||||
const file = new File([], "fileName", { type: "audio/mp3" });
|
||||
await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined);
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.audio",
|
||||
}));
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
null,
|
||||
expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.audio",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should default to name 'Attachment' if file doesn't have a name", async () => {
|
||||
mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" });
|
||||
const file = new File([], "", { type: "text/plain" });
|
||||
await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined);
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.file",
|
||||
body: "Attachment",
|
||||
}));
|
||||
expect(client.sendMessage).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
null,
|
||||
expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
msgtype: "m.file",
|
||||
body: "Attachment",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should keep RoomUpload's total and loaded values up to date", async () => {
|
||||
@@ -196,10 +214,9 @@ describe("ContentMessages", () => {
|
||||
const roomId = "!roomId:server";
|
||||
|
||||
beforeEach(() => {
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(<T>(
|
||||
roomId: string,
|
||||
fn: (actualRoomId: string) => Promise<T>,
|
||||
) => fn(roomId));
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(
|
||||
<T>(roomId: string, fn: (actualRoomId: string) => Promise<T>) => fn(roomId),
|
||||
);
|
||||
});
|
||||
|
||||
it("should return only uploads for the given relation", async () => {
|
||||
@@ -284,14 +301,19 @@ describe("uploadFile", () => {
|
||||
const res = await uploadFile(client, "!roomId:server", file, progressHandler);
|
||||
|
||||
expect(res.url).toBeFalsy();
|
||||
expect(res.file).toEqual(expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
}));
|
||||
expect(res.file).toEqual(
|
||||
expect.objectContaining({
|
||||
url: "mxc://server/file",
|
||||
}),
|
||||
);
|
||||
expect(encrypt.encryptAttachment).toHaveBeenCalled();
|
||||
expect(client.uploadContent).toHaveBeenCalledWith(expect.any(Blob), expect.objectContaining({
|
||||
progressHandler,
|
||||
includeFilename: false,
|
||||
}));
|
||||
expect(client.uploadContent).toHaveBeenCalledWith(
|
||||
expect.any(Blob),
|
||||
expect.objectContaining({
|
||||
progressHandler,
|
||||
includeFilename: false,
|
||||
}),
|
||||
);
|
||||
expect(mocked(client.uploadContent).mock.calls[0][0]).not.toBe(file);
|
||||
});
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixEvent } from 'matrix-js-sdk/src/matrix';
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { DecryptionFailureTracker } from '../src/DecryptionFailureTracker';
|
||||
import { DecryptionFailureTracker } from "../src/DecryptionFailureTracker";
|
||||
|
||||
class MockDecryptionError extends Error {
|
||||
constructor(code) {
|
||||
super();
|
||||
|
||||
this.code = code || 'MOCK_DECRYPTION_ERROR';
|
||||
this.code = code || "MOCK_DECRYPTION_ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +37,15 @@ function createFailedDecryptionEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
describe('DecryptionFailureTracker', function() {
|
||||
it('tracks a failed decryption for a visible event', function(done) {
|
||||
describe("DecryptionFailureTracker", function () {
|
||||
it("tracks a failed decryption for a visible event", function (done) {
|
||||
const failedDecryptionEvent = createFailedDecryptionEvent();
|
||||
|
||||
let count = 0;
|
||||
const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError");
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total) => (count += total),
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
tracker.addVisibleEvent(failedDecryptionEvent);
|
||||
|
||||
@@ -55,24 +58,27 @@ describe('DecryptionFailureTracker', function() {
|
||||
// Immediately track the newest failures
|
||||
tracker.trackFailures();
|
||||
|
||||
expect(count).not.toBe(0, 'should track a failure for an event that failed decryption');
|
||||
expect(count).not.toBe(0, "should track a failure for an event that failed decryption");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('tracks a failed decryption with expected raw error for a visible event', function(done) {
|
||||
it("tracks a failed decryption with expected raw error for a visible event", function (done) {
|
||||
const failedDecryptionEvent = createFailedDecryptionEvent();
|
||||
|
||||
let count = 0;
|
||||
let reportedRawCode = "";
|
||||
const tracker = new DecryptionFailureTracker((total, errcode, rawCode) => {
|
||||
count += total;
|
||||
reportedRawCode = rawCode;
|
||||
}, () => "UnknownError");
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total, errcode, rawCode) => {
|
||||
count += total;
|
||||
reportedRawCode = rawCode;
|
||||
},
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
tracker.addVisibleEvent(failedDecryptionEvent);
|
||||
|
||||
const err = new MockDecryptionError('INBOUND_SESSION_MISMATCH_ROOM_ID');
|
||||
const err = new MockDecryptionError("INBOUND_SESSION_MISMATCH_ROOM_ID");
|
||||
tracker.eventDecrypted(failedDecryptionEvent, err);
|
||||
|
||||
// Pretend "now" is Infinity
|
||||
@@ -81,17 +87,20 @@ describe('DecryptionFailureTracker', function() {
|
||||
// Immediately track the newest failures
|
||||
tracker.trackFailures();
|
||||
|
||||
expect(count).not.toBe(0, 'should track a failure for an event that failed decryption');
|
||||
expect(reportedRawCode).toBe('INBOUND_SESSION_MISMATCH_ROOM_ID', 'Should add the rawCode to the event context');
|
||||
expect(count).not.toBe(0, "should track a failure for an event that failed decryption");
|
||||
expect(reportedRawCode).toBe("INBOUND_SESSION_MISMATCH_ROOM_ID", "Should add the rawCode to the event context");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('tracks a failed decryption for an event that becomes visible later', function(done) {
|
||||
it("tracks a failed decryption for an event that becomes visible later", function (done) {
|
||||
const failedDecryptionEvent = createFailedDecryptionEvent();
|
||||
|
||||
let count = 0;
|
||||
const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError");
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total) => (count += total),
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
const err = new MockDecryptionError();
|
||||
tracker.eventDecrypted(failedDecryptionEvent, err);
|
||||
@@ -104,16 +113,19 @@ describe('DecryptionFailureTracker', function() {
|
||||
// Immediately track the newest failures
|
||||
tracker.trackFailures();
|
||||
|
||||
expect(count).not.toBe(0, 'should track a failure for an event that failed decryption');
|
||||
expect(count).not.toBe(0, "should track a failure for an event that failed decryption");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('does not track a failed decryption for an event that never becomes visible', function(done) {
|
||||
it("does not track a failed decryption for an event that never becomes visible", function (done) {
|
||||
const failedDecryptionEvent = createFailedDecryptionEvent();
|
||||
|
||||
let count = 0;
|
||||
const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError");
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total) => (count += total),
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
const err = new MockDecryptionError();
|
||||
tracker.eventDecrypted(failedDecryptionEvent, err);
|
||||
@@ -124,16 +136,19 @@ describe('DecryptionFailureTracker', function() {
|
||||
// Immediately track the newest failures
|
||||
tracker.trackFailures();
|
||||
|
||||
expect(count).toBe(0, 'should not track a failure for an event that never became visible');
|
||||
expect(count).toBe(0, "should not track a failure for an event that never became visible");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('does not track a failed decryption where the event is subsequently successfully decrypted', (done) => {
|
||||
it("does not track a failed decryption where the event is subsequently successfully decrypted", (done) => {
|
||||
const decryptedEvent = createFailedDecryptionEvent();
|
||||
const tracker = new DecryptionFailureTracker((total) => {
|
||||
expect(true).toBe(false, 'should not track an event that has since been decrypted correctly');
|
||||
}, () => "UnknownError");
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total) => {
|
||||
expect(true).toBe(false, "should not track an event that has since been decrypted correctly");
|
||||
},
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
tracker.addVisibleEvent(decryptedEvent);
|
||||
|
||||
@@ -152,36 +167,45 @@ describe('DecryptionFailureTracker', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
it('does not track a failed decryption where the event is subsequently successfully decrypted ' +
|
||||
'and later becomes visible', (done) => {
|
||||
const decryptedEvent = createFailedDecryptionEvent();
|
||||
const tracker = new DecryptionFailureTracker((total) => {
|
||||
expect(true).toBe(false, 'should not track an event that has since been decrypted correctly');
|
||||
}, () => "UnknownError");
|
||||
it(
|
||||
"does not track a failed decryption where the event is subsequently successfully decrypted " +
|
||||
"and later becomes visible",
|
||||
(done) => {
|
||||
const decryptedEvent = createFailedDecryptionEvent();
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total) => {
|
||||
expect(true).toBe(false, "should not track an event that has since been decrypted correctly");
|
||||
},
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
const err = new MockDecryptionError();
|
||||
tracker.eventDecrypted(decryptedEvent, err);
|
||||
const err = new MockDecryptionError();
|
||||
tracker.eventDecrypted(decryptedEvent, err);
|
||||
|
||||
// Indicate successful decryption: clear data can be anything where the msgtype is not m.bad.encrypted
|
||||
decryptedEvent.setClearData({});
|
||||
tracker.eventDecrypted(decryptedEvent, null);
|
||||
// Indicate successful decryption: clear data can be anything where the msgtype is not m.bad.encrypted
|
||||
decryptedEvent.setClearData({});
|
||||
tracker.eventDecrypted(decryptedEvent, null);
|
||||
|
||||
tracker.addVisibleEvent(decryptedEvent);
|
||||
tracker.addVisibleEvent(decryptedEvent);
|
||||
|
||||
// Pretend "now" is Infinity
|
||||
tracker.checkFailures(Infinity);
|
||||
// Pretend "now" is Infinity
|
||||
tracker.checkFailures(Infinity);
|
||||
|
||||
// Immediately track the newest failures
|
||||
tracker.trackFailures();
|
||||
done();
|
||||
});
|
||||
// Immediately track the newest failures
|
||||
tracker.trackFailures();
|
||||
done();
|
||||
},
|
||||
);
|
||||
|
||||
it('only tracks a single failure per event, despite multiple failed decryptions for multiple events', (done) => {
|
||||
it("only tracks a single failure per event, despite multiple failed decryptions for multiple events", (done) => {
|
||||
const decryptedEvent = createFailedDecryptionEvent();
|
||||
const decryptedEvent2 = createFailedDecryptionEvent();
|
||||
|
||||
let count = 0;
|
||||
const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError");
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total) => (count += total),
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
tracker.addVisibleEvent(decryptedEvent);
|
||||
|
||||
@@ -206,16 +230,19 @@ describe('DecryptionFailureTracker', function() {
|
||||
tracker.trackFailures();
|
||||
tracker.trackFailures();
|
||||
|
||||
expect(count).toBe(2, count + ' failures tracked, should only track a single failure per event');
|
||||
expect(count).toBe(2, count + " failures tracked, should only track a single failure per event");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should not track a failure for an event that was tracked previously', (done) => {
|
||||
it("should not track a failure for an event that was tracked previously", (done) => {
|
||||
const decryptedEvent = createFailedDecryptionEvent();
|
||||
|
||||
let count = 0;
|
||||
const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError");
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total) => (count += total),
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
tracker.addVisibleEvent(decryptedEvent);
|
||||
|
||||
@@ -233,19 +260,22 @@ describe('DecryptionFailureTracker', function() {
|
||||
|
||||
tracker.trackFailures();
|
||||
|
||||
expect(count).toBe(1, 'should only track a single failure per event');
|
||||
expect(count).toBe(1, "should only track a single failure per event");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
xit('should not track a failure for an event that was tracked in a previous session', (done) => {
|
||||
xit("should not track a failure for an event that was tracked in a previous session", (done) => {
|
||||
// This test uses localStorage, clear it beforehand
|
||||
localStorage.clear();
|
||||
|
||||
const decryptedEvent = createFailedDecryptionEvent();
|
||||
|
||||
let count = 0;
|
||||
const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError");
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total) => (count += total),
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
tracker.addVisibleEvent(decryptedEvent);
|
||||
|
||||
@@ -260,7 +290,10 @@ describe('DecryptionFailureTracker', function() {
|
||||
tracker.trackFailures();
|
||||
|
||||
// Simulate the browser refreshing by destroying tracker and creating a new tracker
|
||||
const secondTracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError");
|
||||
const secondTracker = new DecryptionFailureTracker(
|
||||
(total) => (count += total),
|
||||
() => "UnknownError",
|
||||
);
|
||||
|
||||
secondTracker.addVisibleEvent(decryptedEvent);
|
||||
|
||||
@@ -270,24 +303,24 @@ describe('DecryptionFailureTracker', function() {
|
||||
secondTracker.checkFailures(Infinity);
|
||||
secondTracker.trackFailures();
|
||||
|
||||
expect(count).toBe(1, count + ' failures tracked, should only track a single failure per event');
|
||||
expect(count).toBe(1, count + " failures tracked, should only track a single failure per event");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should count different error codes separately for multiple failures with different error codes', () => {
|
||||
it("should count different error codes separately for multiple failures with different error codes", () => {
|
||||
const counts = {};
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total, errorCode) => counts[errorCode] = (counts[errorCode] || 0) + total,
|
||||
(error) => error === "UnknownError" ? "UnknownError" : "OlmKeysNotSentError",
|
||||
(total, errorCode) => (counts[errorCode] = (counts[errorCode] || 0) + total),
|
||||
(error) => (error === "UnknownError" ? "UnknownError" : "OlmKeysNotSentError"),
|
||||
);
|
||||
|
||||
const decryptedEvent1 = createFailedDecryptionEvent();
|
||||
const decryptedEvent2 = createFailedDecryptionEvent();
|
||||
const decryptedEvent3 = createFailedDecryptionEvent();
|
||||
|
||||
const error1 = new MockDecryptionError('UnknownError');
|
||||
const error2 = new MockDecryptionError('OlmKeysNotSentError');
|
||||
const error1 = new MockDecryptionError("UnknownError");
|
||||
const error2 = new MockDecryptionError("OlmKeysNotSentError");
|
||||
|
||||
tracker.addVisibleEvent(decryptedEvent1);
|
||||
tracker.addVisibleEvent(decryptedEvent2);
|
||||
@@ -305,23 +338,23 @@ describe('DecryptionFailureTracker', function() {
|
||||
tracker.trackFailures();
|
||||
|
||||
//expect(counts['UnknownError']).toBe(1, 'should track one UnknownError');
|
||||
expect(counts['OlmKeysNotSentError']).toBe(2, 'should track two OlmKeysNotSentError');
|
||||
expect(counts["OlmKeysNotSentError"]).toBe(2, "should track two OlmKeysNotSentError");
|
||||
});
|
||||
|
||||
it('should aggregate error codes correctly', () => {
|
||||
it("should aggregate error codes correctly", () => {
|
||||
const counts = {};
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total, errorCode) => counts[errorCode] = (counts[errorCode] || 0) + total,
|
||||
(errorCode) => 'OlmUnspecifiedError',
|
||||
(total, errorCode) => (counts[errorCode] = (counts[errorCode] || 0) + total),
|
||||
(errorCode) => "OlmUnspecifiedError",
|
||||
);
|
||||
|
||||
const decryptedEvent1 = createFailedDecryptionEvent();
|
||||
const decryptedEvent2 = createFailedDecryptionEvent();
|
||||
const decryptedEvent3 = createFailedDecryptionEvent();
|
||||
|
||||
const error1 = new MockDecryptionError('ERROR_CODE_1');
|
||||
const error2 = new MockDecryptionError('ERROR_CODE_2');
|
||||
const error3 = new MockDecryptionError('ERROR_CODE_3');
|
||||
const error1 = new MockDecryptionError("ERROR_CODE_1");
|
||||
const error2 = new MockDecryptionError("ERROR_CODE_2");
|
||||
const error3 = new MockDecryptionError("ERROR_CODE_3");
|
||||
|
||||
tracker.addVisibleEvent(decryptedEvent1);
|
||||
tracker.addVisibleEvent(decryptedEvent2);
|
||||
@@ -336,20 +369,22 @@ describe('DecryptionFailureTracker', function() {
|
||||
|
||||
tracker.trackFailures();
|
||||
|
||||
expect(counts['OlmUnspecifiedError'])
|
||||
.toBe(3, 'should track three OlmUnspecifiedError, got ' + counts['OlmUnspecifiedError']);
|
||||
expect(counts["OlmUnspecifiedError"]).toBe(
|
||||
3,
|
||||
"should track three OlmUnspecifiedError, got " + counts["OlmUnspecifiedError"],
|
||||
);
|
||||
});
|
||||
|
||||
it('should remap error codes correctly', () => {
|
||||
it("should remap error codes correctly", () => {
|
||||
const counts = {};
|
||||
const tracker = new DecryptionFailureTracker(
|
||||
(total, errorCode) => counts[errorCode] = (counts[errorCode] || 0) + total,
|
||||
(errorCode) => Array.from(errorCode).reverse().join(''),
|
||||
(total, errorCode) => (counts[errorCode] = (counts[errorCode] || 0) + total),
|
||||
(errorCode) => Array.from(errorCode).reverse().join(""),
|
||||
);
|
||||
|
||||
const decryptedEvent = createFailedDecryptionEvent();
|
||||
|
||||
const error = new MockDecryptionError('ERROR_CODE_1');
|
||||
const error = new MockDecryptionError("ERROR_CODE_1");
|
||||
|
||||
tracker.addVisibleEvent(decryptedEvent);
|
||||
|
||||
@@ -360,7 +395,6 @@ describe('DecryptionFailureTracker', function() {
|
||||
|
||||
tracker.trackFailures();
|
||||
|
||||
expect(counts['1_EDOC_RORRE'])
|
||||
.toBe(1, 'should track remapped error code');
|
||||
expect(counts["1_EDOC_RORRE"]).toBe(1, "should track remapped error code");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -46,34 +45,35 @@ jest.mock("../src/dispatcher/dispatcher", () => ({
|
||||
}));
|
||||
|
||||
jest.mock("../src/SecurityManager", () => ({
|
||||
isSecretStorageBeingAccessed: jest.fn(), accessSecretStorage: jest.fn(),
|
||||
isSecretStorageBeingAccessed: jest.fn(),
|
||||
accessSecretStorage: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../src/utils/device/snoozeBulkUnverifiedDeviceReminder", () => ({
|
||||
isBulkUnverifiedDeviceReminderSnoozed: jest.fn(),
|
||||
}));
|
||||
|
||||
const userId = '@user:server';
|
||||
const deviceId = 'my-device-id';
|
||||
const userId = "@user:server";
|
||||
const deviceId = "my-device-id";
|
||||
const mockDispatcher = mocked(dis);
|
||||
const flushPromises = async () => await new Promise(process.nextTick);
|
||||
|
||||
describe('DeviceListener', () => {
|
||||
describe("DeviceListener", () => {
|
||||
let mockClient: Mocked<MatrixClient> | undefined;
|
||||
|
||||
// spy on various toasts' hide and show functions
|
||||
// easier than mocking
|
||||
jest.spyOn(SetupEncryptionToast, 'showToast');
|
||||
jest.spyOn(SetupEncryptionToast, 'hideToast');
|
||||
jest.spyOn(BulkUnverifiedSessionsToast, 'showToast');
|
||||
jest.spyOn(BulkUnverifiedSessionsToast, 'hideToast');
|
||||
jest.spyOn(UnverifiedSessionToast, 'showToast');
|
||||
jest.spyOn(UnverifiedSessionToast, 'hideToast');
|
||||
jest.spyOn(SetupEncryptionToast, "showToast");
|
||||
jest.spyOn(SetupEncryptionToast, "hideToast");
|
||||
jest.spyOn(BulkUnverifiedSessionsToast, "showToast");
|
||||
jest.spyOn(BulkUnverifiedSessionsToast, "hideToast");
|
||||
jest.spyOn(UnverifiedSessionToast, "showToast");
|
||||
jest.spyOn(UnverifiedSessionToast, "hideToast");
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
mockPlatformPeg({
|
||||
getAppVersion: jest.fn().mockResolvedValue('1.2.3'),
|
||||
getAppVersion: jest.fn().mockResolvedValue("1.2.3"),
|
||||
});
|
||||
mockClient = getMockClientWithEventEmitter({
|
||||
isGuest: jest.fn(),
|
||||
@@ -98,8 +98,8 @@ describe('DeviceListener', () => {
|
||||
getAccountData: jest.fn(),
|
||||
checkDeviceTrust: jest.fn().mockReturnValue(new DeviceTrustLevel(false, false, false, false)),
|
||||
});
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient);
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
mocked(isBulkUnverifiedDeviceReminderSnoozed).mockClear().mockReturnValue(false);
|
||||
});
|
||||
|
||||
@@ -110,51 +110,46 @@ describe('DeviceListener', () => {
|
||||
return instance;
|
||||
};
|
||||
|
||||
describe('client information', () => {
|
||||
it('watches device client information setting', async () => {
|
||||
const watchSettingSpy = jest.spyOn(SettingsStore, 'watchSetting');
|
||||
const unwatchSettingSpy = jest.spyOn(SettingsStore, 'unwatchSetting');
|
||||
describe("client information", () => {
|
||||
it("watches device client information setting", async () => {
|
||||
const watchSettingSpy = jest.spyOn(SettingsStore, "watchSetting");
|
||||
const unwatchSettingSpy = jest.spyOn(SettingsStore, "unwatchSetting");
|
||||
const deviceListener = await createAndStart();
|
||||
|
||||
expect(watchSettingSpy).toHaveBeenCalledWith(
|
||||
'deviceClientInformationOptIn', null, expect.any(Function),
|
||||
);
|
||||
expect(watchSettingSpy).toHaveBeenCalledWith("deviceClientInformationOptIn", null, expect.any(Function));
|
||||
|
||||
deviceListener.stop();
|
||||
|
||||
expect(unwatchSettingSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when device client information feature is enabled', () => {
|
||||
describe("when device client information feature is enabled", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockImplementation(
|
||||
settingName => settingName === 'deviceClientInformationOptIn',
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(
|
||||
(settingName) => settingName === "deviceClientInformationOptIn",
|
||||
);
|
||||
});
|
||||
it('saves client information on start', async () => {
|
||||
it("saves client information on start", async () => {
|
||||
await createAndStart();
|
||||
|
||||
expect(mockClient!.setAccountData).toHaveBeenCalledWith(
|
||||
`io.element.matrix_client_information.${deviceId}`,
|
||||
{ name: 'Element', url: 'localhost', version: '1.2.3' },
|
||||
{ name: "Element", url: "localhost", version: "1.2.3" },
|
||||
);
|
||||
});
|
||||
|
||||
it('catches error and logs when saving client information fails', async () => {
|
||||
const errorLogSpy = jest.spyOn(logger, 'error');
|
||||
const error = new Error('oups');
|
||||
it("catches error and logs when saving client information fails", async () => {
|
||||
const errorLogSpy = jest.spyOn(logger, "error");
|
||||
const error = new Error("oups");
|
||||
mockClient!.setAccountData.mockRejectedValue(error);
|
||||
|
||||
// doesn't throw
|
||||
await createAndStart();
|
||||
|
||||
expect(errorLogSpy).toHaveBeenCalledWith(
|
||||
'Failed to update client information',
|
||||
error,
|
||||
);
|
||||
expect(errorLogSpy).toHaveBeenCalledWith("Failed to update client information", error);
|
||||
});
|
||||
|
||||
it('saves client information on logged in action', async () => {
|
||||
it("saves client information on logged in action", async () => {
|
||||
const instance = await createAndStart();
|
||||
|
||||
mockClient!.setAccountData.mockClear();
|
||||
@@ -166,29 +161,30 @@ describe('DeviceListener', () => {
|
||||
|
||||
expect(mockClient!.setAccountData).toHaveBeenCalledWith(
|
||||
`io.element.matrix_client_information.${deviceId}`,
|
||||
{ name: 'Element', url: 'localhost', version: '1.2.3' },
|
||||
{ name: "Element", url: "localhost", version: "1.2.3" },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when device client information feature is disabled', () => {
|
||||
const clientInfoEvent = new MatrixEvent({ type: `io.element.matrix_client_information.${deviceId}`,
|
||||
content: { name: 'hello' },
|
||||
describe("when device client information feature is disabled", () => {
|
||||
const clientInfoEvent = new MatrixEvent({
|
||||
type: `io.element.matrix_client_information.${deviceId}`,
|
||||
content: { name: "hello" },
|
||||
});
|
||||
const emptyClientInfoEvent = new MatrixEvent({ type: `io.element.matrix_client_information.${deviceId}` });
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
|
||||
mockClient!.getAccountData.mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
it('does not save client information on start', async () => {
|
||||
it("does not save client information on start", async () => {
|
||||
await createAndStart();
|
||||
|
||||
expect(mockClient!.setAccountData).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('removes client information on start if it exists', async () => {
|
||||
it("removes client information on start if it exists", async () => {
|
||||
mockClient!.getAccountData.mockReturnValue(clientInfoEvent);
|
||||
await createAndStart();
|
||||
|
||||
@@ -198,14 +194,14 @@ describe('DeviceListener', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('does not try to remove client info event that are already empty', async () => {
|
||||
it("does not try to remove client info event that are already empty", async () => {
|
||||
mockClient!.getAccountData.mockReturnValue(emptyClientInfoEvent);
|
||||
await createAndStart();
|
||||
|
||||
expect(mockClient!.setAccountData).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not save client information on logged in action', async () => {
|
||||
it("does not save client information on logged in action", async () => {
|
||||
const instance = await createAndStart();
|
||||
|
||||
// @ts-ignore calling private function
|
||||
@@ -216,51 +212,48 @@ describe('DeviceListener', () => {
|
||||
expect(mockClient!.setAccountData).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('saves client information after setting is enabled', async () => {
|
||||
const watchSettingSpy = jest.spyOn(SettingsStore, 'watchSetting');
|
||||
it("saves client information after setting is enabled", async () => {
|
||||
const watchSettingSpy = jest.spyOn(SettingsStore, "watchSetting");
|
||||
await createAndStart();
|
||||
|
||||
const [settingName, roomId, callback] = watchSettingSpy.mock.calls[0];
|
||||
expect(settingName).toEqual('deviceClientInformationOptIn');
|
||||
expect(settingName).toEqual("deviceClientInformationOptIn");
|
||||
expect(roomId).toBeNull();
|
||||
|
||||
callback('deviceClientInformationOptIn', null, SettingLevel.DEVICE, SettingLevel.DEVICE, true);
|
||||
callback("deviceClientInformationOptIn", null, SettingLevel.DEVICE, SettingLevel.DEVICE, true);
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(mockClient!.setAccountData).toHaveBeenCalledWith(
|
||||
`io.element.matrix_client_information.${deviceId}`,
|
||||
{ name: 'Element', url: 'localhost', version: '1.2.3' },
|
||||
{ name: "Element", url: "localhost", version: "1.2.3" },
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('recheck', () => {
|
||||
it('does nothing when cross signing feature is not supported', async () => {
|
||||
describe("recheck", () => {
|
||||
it("does nothing when cross signing feature is not supported", async () => {
|
||||
mockClient!.doesServerSupportUnstableFeature.mockResolvedValue(false);
|
||||
await createAndStart();
|
||||
|
||||
expect(mockClient!.isCrossSigningReady).not.toHaveBeenCalled();
|
||||
});
|
||||
it('does nothing when crypto is not enabled', async () => {
|
||||
it("does nothing when crypto is not enabled", async () => {
|
||||
mockClient!.isCryptoEnabled.mockReturnValue(false);
|
||||
await createAndStart();
|
||||
|
||||
expect(mockClient!.isCrossSigningReady).not.toHaveBeenCalled();
|
||||
});
|
||||
it('does nothing when initial sync is not complete', async () => {
|
||||
it("does nothing when initial sync is not complete", async () => {
|
||||
mockClient!.isInitialSyncComplete.mockReturnValue(false);
|
||||
await createAndStart();
|
||||
|
||||
expect(mockClient!.isCrossSigningReady).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('set up encryption', () => {
|
||||
const rooms = [
|
||||
{ roomId: '!room1' },
|
||||
{ roomId: '!room2' },
|
||||
] as unknown as Room[];
|
||||
describe("set up encryption", () => {
|
||||
const rooms = [{ roomId: "!room1" }, { roomId: "!room2" }] as unknown as Room[];
|
||||
|
||||
beforeEach(() => {
|
||||
mockClient!.isCrossSigningReady.mockResolvedValue(false);
|
||||
@@ -269,21 +262,21 @@ describe('DeviceListener', () => {
|
||||
mockClient!.isRoomEncrypted.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('hides setup encryption toast when cross signing and secret storage are ready', async () => {
|
||||
it("hides setup encryption toast when cross signing and secret storage are ready", async () => {
|
||||
mockClient!.isCrossSigningReady.mockResolvedValue(true);
|
||||
mockClient!.isSecretStorageReady.mockResolvedValue(true);
|
||||
await createAndStart();
|
||||
expect(SetupEncryptionToast.hideToast).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('hides setup encryption toast when it is dismissed', async () => {
|
||||
it("hides setup encryption toast when it is dismissed", async () => {
|
||||
const instance = await createAndStart();
|
||||
instance.dismissEncryptionSetup();
|
||||
await flushPromises();
|
||||
expect(SetupEncryptionToast.hideToast).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not do any checks or show any toasts when secret storage is being accessed', async () => {
|
||||
it("does not do any checks or show any toasts when secret storage is being accessed", async () => {
|
||||
mocked(isSecretStorageBeingAccessed).mockReturnValue(true);
|
||||
await createAndStart();
|
||||
|
||||
@@ -291,7 +284,7 @@ describe('DeviceListener', () => {
|
||||
expect(SetupEncryptionToast.showToast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not do any checks or show any toasts when no rooms are encrypted', async () => {
|
||||
it("does not do any checks or show any toasts when no rooms are encrypted", async () => {
|
||||
mockClient!.isRoomEncrypted.mockReturnValue(false);
|
||||
await createAndStart();
|
||||
|
||||
@@ -299,21 +292,22 @@ describe('DeviceListener', () => {
|
||||
expect(SetupEncryptionToast.showToast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when user does not have a cross signing id on this device', () => {
|
||||
describe("when user does not have a cross signing id on this device", () => {
|
||||
beforeEach(() => {
|
||||
mockClient!.getCrossSigningId.mockReturnValue(null);
|
||||
});
|
||||
|
||||
it('shows verify session toast when account has cross signing', async () => {
|
||||
it("shows verify session toast when account has cross signing", async () => {
|
||||
mockClient!.getStoredCrossSigningForUser.mockReturnValue(new CrossSigningInfo(userId));
|
||||
await createAndStart();
|
||||
|
||||
expect(mockClient!.downloadKeys).toHaveBeenCalled();
|
||||
expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith(
|
||||
SetupEncryptionToast.Kind.VERIFY_THIS_SESSION);
|
||||
SetupEncryptionToast.Kind.VERIFY_THIS_SESSION,
|
||||
);
|
||||
});
|
||||
|
||||
it('checks key backup status when when account has cross signing', async () => {
|
||||
it("checks key backup status when when account has cross signing", async () => {
|
||||
mockClient!.getCrossSigningId.mockReturnValue(null);
|
||||
mockClient!.getStoredCrossSigningForUser.mockReturnValue(new CrossSigningInfo(userId));
|
||||
await createAndStart();
|
||||
@@ -322,31 +316,32 @@ describe('DeviceListener', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user does have a cross signing id on this device', () => {
|
||||
describe("when user does have a cross signing id on this device", () => {
|
||||
beforeEach(() => {
|
||||
mockClient!.getCrossSigningId.mockReturnValue('abc');
|
||||
mockClient!.getCrossSigningId.mockReturnValue("abc");
|
||||
});
|
||||
|
||||
it('shows upgrade encryption toast when user has a key backup available', async () => {
|
||||
it("shows upgrade encryption toast when user has a key backup available", async () => {
|
||||
// non falsy response
|
||||
mockClient!.getKeyBackupVersion.mockResolvedValue({} as unknown as IKeyBackupInfo);
|
||||
await createAndStart();
|
||||
|
||||
expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith(
|
||||
SetupEncryptionToast.Kind.UPGRADE_ENCRYPTION);
|
||||
SetupEncryptionToast.Kind.UPGRADE_ENCRYPTION,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('key backup status', () => {
|
||||
it('checks keybackup status when cross signing and secret storage are ready', async () => {
|
||||
describe("key backup status", () => {
|
||||
it("checks keybackup status when cross signing and secret storage are ready", async () => {
|
||||
// default mocks set cross signing and secret storage to ready
|
||||
await createAndStart();
|
||||
expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled();
|
||||
expect(mockDispatcher.dispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('checks keybackup status when setup encryption toast has been dismissed', async () => {
|
||||
it("checks keybackup status when setup encryption toast has been dismissed", async () => {
|
||||
mockClient!.isCrossSigningReady.mockResolvedValue(false);
|
||||
const instance = await createAndStart();
|
||||
|
||||
@@ -356,20 +351,20 @@ describe('DeviceListener', () => {
|
||||
expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not dispatch keybackup event when key backup check is not finished', async () => {
|
||||
it("does not dispatch keybackup event when key backup check is not finished", async () => {
|
||||
// returns null when key backup status hasn't finished being checked
|
||||
mockClient!.getKeyBackupEnabled.mockReturnValue(null);
|
||||
await createAndStart();
|
||||
expect(mockDispatcher.dispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('dispatches keybackup event when key backup is not enabled', async () => {
|
||||
it("dispatches keybackup event when key backup is not enabled", async () => {
|
||||
mockClient!.getKeyBackupEnabled.mockReturnValue(false);
|
||||
await createAndStart();
|
||||
expect(mockDispatcher.dispatch).toHaveBeenCalledWith({ action: Action.ReportKeyBackupNotEnabled });
|
||||
});
|
||||
|
||||
it('does not check key backup status again after check is complete', async () => {
|
||||
it("does not check key backup status again after check is complete", async () => {
|
||||
mockClient!.getKeyBackupEnabled.mockReturnValue(null);
|
||||
const instance = await createAndStart();
|
||||
expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled();
|
||||
@@ -390,43 +385,41 @@ describe('DeviceListener', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('unverified sessions toasts', () => {
|
||||
describe("unverified sessions toasts", () => {
|
||||
const currentDevice = new DeviceInfo(deviceId);
|
||||
const device2 = new DeviceInfo('d2');
|
||||
const device3 = new DeviceInfo('d3');
|
||||
const device2 = new DeviceInfo("d2");
|
||||
const device3 = new DeviceInfo("d3");
|
||||
|
||||
const deviceTrustVerified = new DeviceTrustLevel(true, false, false, false);
|
||||
const deviceTrustUnverified = new DeviceTrustLevel(false, false, false, false);
|
||||
|
||||
beforeEach(() => {
|
||||
mockClient!.isCrossSigningReady.mockResolvedValue(true);
|
||||
mockClient!.getStoredDevicesForUser.mockReturnValue([
|
||||
currentDevice, device2, device3,
|
||||
]);
|
||||
mockClient!.getStoredDevicesForUser.mockReturnValue([currentDevice, device2, device3]);
|
||||
// all devices verified by default
|
||||
mockClient!.checkDeviceTrust.mockReturnValue(deviceTrustVerified);
|
||||
mockClient!.deviceId = currentDevice.deviceId;
|
||||
jest.spyOn(SettingsStore, 'getValue').mockImplementation(
|
||||
settingName => settingName === UIFeature.BulkUnverifiedSessionsReminder,
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(
|
||||
(settingName) => settingName === UIFeature.BulkUnverifiedSessionsReminder,
|
||||
);
|
||||
});
|
||||
describe('bulk unverified sessions toasts', () => {
|
||||
it('hides toast when cross signing is not ready', async () => {
|
||||
describe("bulk unverified sessions toasts", () => {
|
||||
it("hides toast when cross signing is not ready", async () => {
|
||||
mockClient!.isCrossSigningReady.mockResolvedValue(false);
|
||||
await createAndStart();
|
||||
expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled();
|
||||
expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('hides toast when all devices at app start are verified', async () => {
|
||||
it("hides toast when all devices at app start are verified", async () => {
|
||||
await createAndStart();
|
||||
expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled();
|
||||
expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('hides toast when feature is disabled', async () => {
|
||||
it("hides toast when feature is disabled", async () => {
|
||||
// BulkUnverifiedSessionsReminder set to false
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
// currentDevice, device2 are verified, device3 is unverified
|
||||
// ie if reminder was enabled it should be shown
|
||||
mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
|
||||
@@ -442,7 +435,7 @@ describe('DeviceListener', () => {
|
||||
expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('hides toast when current device is unverified', async () => {
|
||||
it("hides toast when current device is unverified", async () => {
|
||||
// device2 verified, current and device3 unverified
|
||||
mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
|
||||
switch (deviceId) {
|
||||
@@ -457,7 +450,7 @@ describe('DeviceListener', () => {
|
||||
expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('hides toast when reminder is snoozed', async () => {
|
||||
it("hides toast when reminder is snoozed", async () => {
|
||||
mocked(isBulkUnverifiedDeviceReminderSnoozed).mockReturnValue(true);
|
||||
// currentDevice, device2 are verified, device3 is unverified
|
||||
mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
|
||||
@@ -474,7 +467,7 @@ describe('DeviceListener', () => {
|
||||
expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows toast with unverified devices at app start', async () => {
|
||||
it("shows toast with unverified devices at app start", async () => {
|
||||
// currentDevice, device2 are verified, device3 is unverified
|
||||
mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
|
||||
switch (deviceId) {
|
||||
@@ -492,7 +485,7 @@ describe('DeviceListener', () => {
|
||||
expect(BulkUnverifiedSessionsToast.hideToast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('hides toast when unverified sessions at app start have been dismissed', async () => {
|
||||
it("hides toast when unverified sessions at app start have been dismissed", async () => {
|
||||
// currentDevice, device2 are verified, device3 is unverified
|
||||
mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
|
||||
switch (deviceId) {
|
||||
@@ -514,7 +507,7 @@ describe('DeviceListener', () => {
|
||||
expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('hides toast when unverified sessions are added after app start', async () => {
|
||||
it("hides toast when unverified sessions are added after app start", async () => {
|
||||
// currentDevice, device2 are verified, device3 is unverified
|
||||
mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
|
||||
switch (deviceId) {
|
||||
@@ -525,17 +518,13 @@ describe('DeviceListener', () => {
|
||||
return deviceTrustUnverified;
|
||||
}
|
||||
});
|
||||
mockClient!.getStoredDevicesForUser.mockReturnValue([
|
||||
currentDevice, device2,
|
||||
]);
|
||||
mockClient!.getStoredDevicesForUser.mockReturnValue([currentDevice, device2]);
|
||||
await createAndStart();
|
||||
|
||||
expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled();
|
||||
|
||||
// add an unverified device
|
||||
mockClient!.getStoredDevicesForUser.mockReturnValue([
|
||||
currentDevice, device2, device3,
|
||||
]);
|
||||
mockClient!.getStoredDevicesForUser.mockReturnValue([currentDevice, device2, device3]);
|
||||
// trigger a recheck
|
||||
mockClient!.emit(CryptoEvent.DevicesUpdated, [userId], false);
|
||||
await flushPromises();
|
||||
|
||||
@@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { mount } from "enzyme";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { topicToHtml } from '../src/HtmlUtils';
|
||||
import SettingsStore from '../src/settings/SettingsStore';
|
||||
import { topicToHtml } from "../src/HtmlUtils";
|
||||
import SettingsStore from "../src/settings/SettingsStore";
|
||||
|
||||
jest.mock("../src/settings/SettingsStore");
|
||||
|
||||
@@ -30,38 +30,39 @@ const enableHtmlTopicFeature = () => {
|
||||
});
|
||||
};
|
||||
|
||||
describe('HtmlUtils', () => {
|
||||
it('converts plain text topic to HTML', () => {
|
||||
const component = mount(<div>{ topicToHtml("pizza", null, null, false) }</div>);
|
||||
describe("HtmlUtils", () => {
|
||||
it("converts plain text topic to HTML", () => {
|
||||
const component = mount(<div>{topicToHtml("pizza", null, null, false)}</div>);
|
||||
const wrapper = component.render();
|
||||
expect(wrapper.children().first().html()).toEqual("pizza");
|
||||
});
|
||||
|
||||
it('converts plain text topic with emoji to HTML', () => {
|
||||
const component = mount(<div>{ topicToHtml("pizza 🍕", null, null, false) }</div>);
|
||||
it("converts plain text topic with emoji to HTML", () => {
|
||||
const component = mount(<div>{topicToHtml("pizza 🍕", null, null, false)}</div>);
|
||||
const wrapper = component.render();
|
||||
expect(wrapper.children().first().html()).toEqual("pizza <span class=\"mx_Emoji\" title=\":pizza:\">🍕</span>");
|
||||
expect(wrapper.children().first().html()).toEqual('pizza <span class="mx_Emoji" title=":pizza:">🍕</span>');
|
||||
});
|
||||
|
||||
it('converts literal HTML topic to HTML', async () => {
|
||||
it("converts literal HTML topic to HTML", async () => {
|
||||
enableHtmlTopicFeature();
|
||||
const component = mount(<div>{ topicToHtml("<b>pizza</b>", null, null, false) }</div>);
|
||||
const component = mount(<div>{topicToHtml("<b>pizza</b>", null, null, false)}</div>);
|
||||
const wrapper = component.render();
|
||||
expect(wrapper.children().first().html()).toEqual("<b>pizza</b>");
|
||||
});
|
||||
|
||||
it('converts true HTML topic to HTML', async () => {
|
||||
it("converts true HTML topic to HTML", async () => {
|
||||
enableHtmlTopicFeature();
|
||||
const component = mount(<div>{ topicToHtml("**pizza**", "<b>pizza</b>", null, false) }</div>);
|
||||
const component = mount(<div>{topicToHtml("**pizza**", "<b>pizza</b>", null, false)}</div>);
|
||||
const wrapper = component.render();
|
||||
expect(wrapper.children().first().html()).toEqual("<b>pizza</b>");
|
||||
});
|
||||
|
||||
it('converts true HTML topic with emoji to HTML', async () => {
|
||||
it("converts true HTML topic with emoji to HTML", async () => {
|
||||
enableHtmlTopicFeature();
|
||||
const component = mount(<div>{ topicToHtml("**pizza** 🍕", "<b>pizza</b> 🍕", null, false) }</div>);
|
||||
const component = mount(<div>{topicToHtml("**pizza** 🍕", "<b>pizza</b> 🍕", null, false)}</div>);
|
||||
const wrapper = component.render();
|
||||
expect(wrapper.children().first().html())
|
||||
.toEqual("<b>pizza</b> <span class=\"mx_Emoji\" title=\":pizza:\">🍕</span>");
|
||||
expect(wrapper.children().first().html()).toEqual(
|
||||
'<b>pizza</b> <span class="mx_Emoji" title=":pizza:">🍕</span>',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,14 +14,17 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { isKeyComboMatch, KeyCombo } from '../src/KeyBindingsManager';
|
||||
import { isKeyComboMatch, KeyCombo } from "../src/KeyBindingsManager";
|
||||
|
||||
function mockKeyEvent(key: string, modifiers?: {
|
||||
ctrlKey?: boolean;
|
||||
altKey?: boolean;
|
||||
shiftKey?: boolean;
|
||||
metaKey?: boolean;
|
||||
}): KeyboardEvent {
|
||||
function mockKeyEvent(
|
||||
key: string,
|
||||
modifiers?: {
|
||||
ctrlKey?: boolean;
|
||||
altKey?: boolean;
|
||||
shiftKey?: boolean;
|
||||
metaKey?: boolean;
|
||||
},
|
||||
): KeyboardEvent {
|
||||
return {
|
||||
key,
|
||||
ctrlKey: modifiers?.ctrlKey ?? false,
|
||||
@@ -31,121 +34,140 @@ function mockKeyEvent(key: string, modifiers?: {
|
||||
} as KeyboardEvent;
|
||||
}
|
||||
|
||||
describe('KeyBindingsManager', () => {
|
||||
it('should match basic key combo', () => {
|
||||
describe("KeyBindingsManager", () => {
|
||||
it("should match basic key combo", () => {
|
||||
const combo1: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
};
|
||||
expect(isKeyComboMatch(mockKeyEvent('k'), combo1, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n'), combo1, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k"), combo1, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("n"), combo1, false)).toBe(false);
|
||||
});
|
||||
|
||||
it('should match key + modifier key combo', () => {
|
||||
it("should match key + modifier key combo", () => {
|
||||
const combo: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
ctrlKey: true,
|
||||
};
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true }), combo, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k'), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { shiftKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { shiftKey: true, metaKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true }), combo, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k"), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { shiftKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { shiftKey: true, metaKey: true }), combo, false)).toBe(false);
|
||||
|
||||
const combo2: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
metaKey: true,
|
||||
};
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true }), combo2, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n', { metaKey: true }), combo2, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k'), combo2, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { altKey: true, metaKey: true }), combo2, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true }), combo2, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("n", { metaKey: true }), combo2, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k"), combo2, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { altKey: true, metaKey: true }), combo2, false)).toBe(false);
|
||||
|
||||
const combo3: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
altKey: true,
|
||||
};
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { altKey: true }), combo3, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n', { altKey: true }), combo3, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k'), combo3, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, metaKey: true }), combo3, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { altKey: true }), combo3, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("n", { altKey: true }), combo3, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k"), combo3, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, metaKey: true }), combo3, false)).toBe(false);
|
||||
|
||||
const combo4: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
shiftKey: true,
|
||||
};
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { shiftKey: true }), combo4, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n', { shiftKey: true }), combo4, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k'), combo4, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { shiftKey: true, ctrlKey: true }), combo4, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { shiftKey: true }), combo4, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("n", { shiftKey: true }), combo4, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k"), combo4, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { shiftKey: true, ctrlKey: true }), combo4, false)).toBe(false);
|
||||
});
|
||||
|
||||
it('should match key + multiple modifiers key combo', () => {
|
||||
it("should match key + multiple modifiers key combo", () => {
|
||||
const combo: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
ctrlKey: true,
|
||||
altKey: true,
|
||||
};
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, altKey: true }), combo, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true, altKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, metaKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, metaKey: true, shiftKey: true }), combo,
|
||||
false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, altKey: true }), combo, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true, altKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, metaKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, metaKey: true, shiftKey: true }), combo, false)).toBe(
|
||||
false,
|
||||
);
|
||||
|
||||
const combo2: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
altKey: true,
|
||||
};
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, shiftKey: true, altKey: true }), combo2,
|
||||
false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true, shiftKey: true, altKey: true }), combo2,
|
||||
false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, metaKey: true }), combo2, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k',
|
||||
{ ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }), combo2, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, shiftKey: true, altKey: true }), combo2, false)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true, shiftKey: true, altKey: true }), combo2, false)).toBe(
|
||||
false,
|
||||
);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, metaKey: true }), combo2, false)).toBe(false);
|
||||
expect(
|
||||
isKeyComboMatch(
|
||||
mockKeyEvent("k", { ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }),
|
||||
combo2,
|
||||
false,
|
||||
),
|
||||
).toBe(false);
|
||||
|
||||
const combo3: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
};
|
||||
expect(isKeyComboMatch(mockKeyEvent('k',
|
||||
{ ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }), combo3, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n',
|
||||
{ ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }), combo3, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k',
|
||||
{ ctrlKey: true, shiftKey: true, altKey: true }), combo3, false)).toBe(false);
|
||||
expect(
|
||||
isKeyComboMatch(
|
||||
mockKeyEvent("k", { ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }),
|
||||
combo3,
|
||||
false,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isKeyComboMatch(
|
||||
mockKeyEvent("n", { ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }),
|
||||
combo3,
|
||||
false,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, shiftKey: true, altKey: true }), combo3, false)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('should match ctrlOrMeta key combo', () => {
|
||||
it("should match ctrlOrMeta key combo", () => {
|
||||
const combo: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
ctrlOrCmdKey: true,
|
||||
};
|
||||
// PC:
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true }), combo, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true }), combo, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true }), combo, false)).toBe(false);
|
||||
// MAC:
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true }), combo, true)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true }), combo, true)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true }), combo, true)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true }), combo, true)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true }), combo, true)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true }), combo, true)).toBe(false);
|
||||
});
|
||||
|
||||
it('should match advanced ctrlOrMeta key combo', () => {
|
||||
it("should match advanced ctrlOrMeta key combo", () => {
|
||||
const combo: KeyCombo = {
|
||||
key: 'k',
|
||||
key: "k",
|
||||
ctrlOrCmdKey: true,
|
||||
altKey: true,
|
||||
};
|
||||
// PC:
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, altKey: true }), combo, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true, altKey: true }), combo, false)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, altKey: true }), combo, false)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true, altKey: true }), combo, false)).toBe(false);
|
||||
// MAC:
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true, altKey: true }), combo, true)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, altKey: true }), combo, true)).toBe(false);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true, altKey: true }), combo, true)).toBe(true);
|
||||
expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, altKey: true }), combo, true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,23 +21,28 @@ import {
|
||||
PushRuleKind,
|
||||
RuleId,
|
||||
TweakName,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
import { CallEvent, CallState, CallType, MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||
import EventEmitter from 'events';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { CallEventHandlerEvent } from 'matrix-js-sdk/src/webrtc/callEventHandler';
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { CallEvent, CallState, CallType, MatrixCall } from "matrix-js-sdk/src/webrtc/call";
|
||||
import EventEmitter from "events";
|
||||
import { mocked } from "jest-mock";
|
||||
import { CallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/callEventHandler";
|
||||
|
||||
import LegacyCallHandler, {
|
||||
LegacyCallHandlerEvent, AudioID, PROTOCOL_PSTN, PROTOCOL_PSTN_PREFIXED, PROTOCOL_SIP_NATIVE, PROTOCOL_SIP_VIRTUAL,
|
||||
} from '../src/LegacyCallHandler';
|
||||
import { stubClient, mkStubRoom, untilDispatch } from './test-utils';
|
||||
import { MatrixClientPeg } from '../src/MatrixClientPeg';
|
||||
import DMRoomMap from '../src/utils/DMRoomMap';
|
||||
import SdkConfig from '../src/SdkConfig';
|
||||
LegacyCallHandlerEvent,
|
||||
AudioID,
|
||||
PROTOCOL_PSTN,
|
||||
PROTOCOL_PSTN_PREFIXED,
|
||||
PROTOCOL_SIP_NATIVE,
|
||||
PROTOCOL_SIP_VIRTUAL,
|
||||
} from "../src/LegacyCallHandler";
|
||||
import { stubClient, mkStubRoom, untilDispatch } from "./test-utils";
|
||||
import { MatrixClientPeg } from "../src/MatrixClientPeg";
|
||||
import DMRoomMap from "../src/utils/DMRoomMap";
|
||||
import SdkConfig from "../src/SdkConfig";
|
||||
import { Action } from "../src/dispatcher/actions";
|
||||
import { getFunctionalMembers } from "../src/utils/room/getFunctionalMembers";
|
||||
import SettingsStore from '../src/settings/SettingsStore';
|
||||
import { UIFeature } from '../src/settings/UIFeature';
|
||||
import SettingsStore from "../src/settings/SettingsStore";
|
||||
import { UIFeature } from "../src/settings/UIFeature";
|
||||
|
||||
jest.mock("../src/utils/room/getFunctionalMembers", () => ({
|
||||
getFunctionalMembers: jest.fn(),
|
||||
@@ -67,34 +72,34 @@ const VIRTUAL_ROOM_BOB = "$virtual_bob_room:example.org";
|
||||
const BOB_PHONE_NUMBER = "01818118181";
|
||||
|
||||
function mkStubDM(roomId, userId) {
|
||||
const room = mkStubRoom(roomId, 'room', MatrixClientPeg.get());
|
||||
const room = mkStubRoom(roomId, "room", MatrixClientPeg.get());
|
||||
room.getJoinedMembers = jest.fn().mockReturnValue([
|
||||
{
|
||||
userId: '@me:example.org',
|
||||
name: 'Member',
|
||||
rawDisplayName: 'Member',
|
||||
userId: "@me:example.org",
|
||||
name: "Member",
|
||||
rawDisplayName: "Member",
|
||||
roomId: roomId,
|
||||
membership: 'join',
|
||||
getAvatarUrl: () => 'mxc://avatar.url/image.png',
|
||||
getMxcAvatarUrl: () => 'mxc://avatar.url/image.png',
|
||||
membership: "join",
|
||||
getAvatarUrl: () => "mxc://avatar.url/image.png",
|
||||
getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
|
||||
},
|
||||
{
|
||||
userId: userId,
|
||||
name: 'Member',
|
||||
rawDisplayName: 'Member',
|
||||
name: "Member",
|
||||
rawDisplayName: "Member",
|
||||
roomId: roomId,
|
||||
membership: 'join',
|
||||
getAvatarUrl: () => 'mxc://avatar.url/image.png',
|
||||
getMxcAvatarUrl: () => 'mxc://avatar.url/image.png',
|
||||
membership: "join",
|
||||
getAvatarUrl: () => "mxc://avatar.url/image.png",
|
||||
getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
|
||||
},
|
||||
{
|
||||
userId: FUNCTIONAL_USER,
|
||||
name: 'Bot user',
|
||||
rawDisplayName: 'Bot user',
|
||||
name: "Bot user",
|
||||
rawDisplayName: "Bot user",
|
||||
roomId: roomId,
|
||||
membership: 'join',
|
||||
getAvatarUrl: () => 'mxc://avatar.url/image.png',
|
||||
getMxcAvatarUrl: () => 'mxc://avatar.url/image.png',
|
||||
membership: "join",
|
||||
getAvatarUrl: () => "mxc://avatar.url/image.png",
|
||||
getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
|
||||
},
|
||||
]);
|
||||
room.currentState.getMembers = room.getJoinedMembers;
|
||||
@@ -127,7 +132,7 @@ function untilCallHandlerEvent(callHandler: LegacyCallHandler, event: LegacyCall
|
||||
});
|
||||
}
|
||||
|
||||
describe('LegacyCallHandler', () => {
|
||||
describe("LegacyCallHandler", () => {
|
||||
let dmRoomMap;
|
||||
let callHandler;
|
||||
let audioElement: HTMLAudioElement;
|
||||
@@ -136,11 +141,11 @@ describe('LegacyCallHandler', () => {
|
||||
// what addresses the app has looked up via pstn and native lookup
|
||||
let pstnLookup: string;
|
||||
let nativeLookup: string;
|
||||
const deviceId = 'my-device';
|
||||
const deviceId = "my-device";
|
||||
|
||||
beforeEach(async () => {
|
||||
stubClient();
|
||||
MatrixClientPeg.get().createCall = roomId => {
|
||||
MatrixClientPeg.get().createCall = (roomId) => {
|
||||
if (fakeCall && fakeCall.roomId !== roomId) {
|
||||
throw new Error("Only one call is supported!");
|
||||
}
|
||||
@@ -160,16 +165,14 @@ describe('LegacyCallHandler', () => {
|
||||
callHandler = new LegacyCallHandler();
|
||||
callHandler.start();
|
||||
|
||||
mocked(getFunctionalMembers).mockReturnValue([
|
||||
FUNCTIONAL_USER,
|
||||
]);
|
||||
mocked(getFunctionalMembers).mockReturnValue([FUNCTIONAL_USER]);
|
||||
|
||||
const nativeRoomAlice = mkStubDM(NATIVE_ROOM_ALICE, NATIVE_ALICE);
|
||||
const nativeRoomBob = mkStubDM(NATIVE_ROOM_BOB, NATIVE_BOB);
|
||||
const nativeRoomCharie = mkStubDM(NATIVE_ROOM_CHARLIE, NATIVE_CHARLIE);
|
||||
const virtualBobRoom = mkStubDM(VIRTUAL_ROOM_BOB, VIRTUAL_BOB);
|
||||
|
||||
MatrixClientPeg.get().getRoom = roomId => {
|
||||
MatrixClientPeg.get().getRoom = (roomId) => {
|
||||
switch (roomId) {
|
||||
case NATIVE_ROOM_ALICE:
|
||||
return nativeRoomAlice;
|
||||
@@ -217,44 +220,50 @@ describe('LegacyCallHandler', () => {
|
||||
|
||||
MatrixClientPeg.get().getThirdpartyUser = (proto, params) => {
|
||||
if ([PROTOCOL_PSTN, PROTOCOL_PSTN_PREFIXED].includes(proto)) {
|
||||
pstnLookup = params['m.id.phone'];
|
||||
return Promise.resolve([{
|
||||
userid: VIRTUAL_BOB,
|
||||
protocol: "m.id.phone",
|
||||
fields: {
|
||||
is_native: true,
|
||||
lookup_success: true,
|
||||
},
|
||||
}]);
|
||||
} else if (proto === PROTOCOL_SIP_NATIVE) {
|
||||
nativeLookup = params['virtual_mxid'];
|
||||
if (params['virtual_mxid'] === VIRTUAL_BOB) {
|
||||
return Promise.resolve([{
|
||||
userid: NATIVE_BOB,
|
||||
protocol: "im.vector.protocol.sip_native",
|
||||
pstnLookup = params["m.id.phone"];
|
||||
return Promise.resolve([
|
||||
{
|
||||
userid: VIRTUAL_BOB,
|
||||
protocol: "m.id.phone",
|
||||
fields: {
|
||||
is_native: true,
|
||||
lookup_success: true,
|
||||
},
|
||||
}]);
|
||||
},
|
||||
]);
|
||||
} else if (proto === PROTOCOL_SIP_NATIVE) {
|
||||
nativeLookup = params["virtual_mxid"];
|
||||
if (params["virtual_mxid"] === VIRTUAL_BOB) {
|
||||
return Promise.resolve([
|
||||
{
|
||||
userid: NATIVE_BOB,
|
||||
protocol: "im.vector.protocol.sip_native",
|
||||
fields: {
|
||||
is_native: true,
|
||||
lookup_success: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
return Promise.resolve([]);
|
||||
} else if (proto === PROTOCOL_SIP_VIRTUAL) {
|
||||
if (params['native_mxid'] === NATIVE_BOB) {
|
||||
return Promise.resolve([{
|
||||
userid: VIRTUAL_BOB,
|
||||
protocol: "im.vector.protocol.sip_virtual",
|
||||
fields: {
|
||||
is_virtual: true,
|
||||
lookup_success: true,
|
||||
if (params["native_mxid"] === NATIVE_BOB) {
|
||||
return Promise.resolve([
|
||||
{
|
||||
userid: VIRTUAL_BOB,
|
||||
protocol: "im.vector.protocol.sip_virtual",
|
||||
fields: {
|
||||
is_virtual: true,
|
||||
lookup_success: true,
|
||||
},
|
||||
},
|
||||
}]);
|
||||
]);
|
||||
}
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
};
|
||||
|
||||
audioElement = document.createElement('audio');
|
||||
audioElement = document.createElement("audio");
|
||||
audioElement.id = "remoteAudio";
|
||||
document.body.appendChild(audioElement);
|
||||
});
|
||||
@@ -271,7 +280,7 @@ describe('LegacyCallHandler', () => {
|
||||
SdkConfig.unset();
|
||||
});
|
||||
|
||||
it('should look up the correct user and start a call in the room when a phone number is dialled', async () => {
|
||||
it("should look up the correct user and start a call in the room when a phone number is dialled", async () => {
|
||||
await callHandler.dialNumber(BOB_PHONE_NUMBER);
|
||||
|
||||
expect(pstnLookup).toEqual(BOB_PHONE_NUMBER);
|
||||
@@ -289,7 +298,7 @@ describe('LegacyCallHandler', () => {
|
||||
expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_BOB);
|
||||
});
|
||||
|
||||
it('should look up the correct user and start a call in the room when a call is transferred', async () => {
|
||||
it("should look up the correct user and start a call in the room when a call is transferred", async () => {
|
||||
// we can pass a very minimal object as as the call since we pass consultFirst=true:
|
||||
// we don't need to actually do any transferring
|
||||
const mockTransferreeCall = { type: CallType.Voice };
|
||||
@@ -304,7 +313,7 @@ describe('LegacyCallHandler', () => {
|
||||
expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_BOB);
|
||||
});
|
||||
|
||||
it('should move calls between rooms when remote asserted identity changes', async () => {
|
||||
it("should move calls between rooms when remote asserted identity changes", async () => {
|
||||
callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice);
|
||||
|
||||
await untilCallHandlerEvent(callHandler, LegacyCallHandlerEvent.CallState);
|
||||
@@ -313,7 +322,7 @@ describe('LegacyCallHandler', () => {
|
||||
expect(callHandler.getCallForRoom(NATIVE_ROOM_ALICE)).toBe(fakeCall);
|
||||
|
||||
let callRoomChangeEventCount = 0;
|
||||
const roomChangePromise = new Promise<void>(resolve => {
|
||||
const roomChangePromise = new Promise<void>((resolve) => {
|
||||
callHandler.addListener(LegacyCallHandlerEvent.CallChangeRoom, () => {
|
||||
++callRoomChangeEventCount;
|
||||
resolve();
|
||||
@@ -355,7 +364,7 @@ describe('LegacyCallHandler', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('LegacyCallHandler without third party protocols', () => {
|
||||
describe("LegacyCallHandler without third party protocols", () => {
|
||||
let dmRoomMap;
|
||||
let callHandler: LegacyCallHandler;
|
||||
let audioElement: HTMLAudioElement;
|
||||
@@ -363,7 +372,7 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
MatrixClientPeg.get().createCall = roomId => {
|
||||
MatrixClientPeg.get().createCall = (roomId) => {
|
||||
if (fakeCall && fakeCall.roomId !== roomId) {
|
||||
throw new Error("Only one call is supported!");
|
||||
}
|
||||
@@ -380,7 +389,7 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
|
||||
const nativeRoomAlice = mkStubDM(NATIVE_ROOM_ALICE, NATIVE_ALICE);
|
||||
|
||||
MatrixClientPeg.get().getRoom = roomId => {
|
||||
MatrixClientPeg.get().getRoom = (roomId) => {
|
||||
switch (roomId) {
|
||||
case NATIVE_ROOM_ALICE:
|
||||
return nativeRoomAlice;
|
||||
@@ -409,7 +418,7 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
throw new Error("Endpoint unsupported.");
|
||||
};
|
||||
|
||||
audioElement = document.createElement('audio');
|
||||
audioElement = document.createElement("audio");
|
||||
audioElement.id = "remoteAudio";
|
||||
document.body.appendChild(audioElement);
|
||||
});
|
||||
@@ -426,7 +435,7 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
SdkConfig.unset();
|
||||
});
|
||||
|
||||
it('should still start a native call', async () => {
|
||||
it("should still start a native call", async () => {
|
||||
callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice);
|
||||
|
||||
await untilCallHandlerEvent(callHandler, LegacyCallHandlerEvent.CallState);
|
||||
@@ -439,8 +448,8 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_ALICE);
|
||||
});
|
||||
|
||||
describe('incoming calls', () => {
|
||||
const roomId = 'test-room-id';
|
||||
describe("incoming calls", () => {
|
||||
const roomId = "test-room-id";
|
||||
|
||||
const mockAudioElement = {
|
||||
play: jest.fn(),
|
||||
@@ -451,35 +460,35 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
} as unknown as HTMLMediaElement;
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.spyOn(SettingsStore, 'getValue').mockImplementation(setting =>
|
||||
setting === UIFeature.Voip);
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === UIFeature.Voip);
|
||||
|
||||
jest.spyOn(MatrixClientPeg.get(), 'supportsVoip').mockReturnValue(true);
|
||||
jest.spyOn(MatrixClientPeg.get(), "supportsVoip").mockReturnValue(true);
|
||||
|
||||
MatrixClientPeg.get().isFallbackICEServerAllowed = jest.fn();
|
||||
MatrixClientPeg.get().prepareToEncrypt = jest.fn();
|
||||
|
||||
MatrixClientPeg.get().pushRules = {
|
||||
global: {
|
||||
[PushRuleKind.Override]: [{
|
||||
rule_id: RuleId.IncomingCall,
|
||||
default: false,
|
||||
enabled: true,
|
||||
actions: [
|
||||
{
|
||||
set_tweak: TweakName.Sound,
|
||||
value: 'ring',
|
||||
},
|
||||
]
|
||||
,
|
||||
}],
|
||||
[PushRuleKind.Override]: [
|
||||
{
|
||||
rule_id: RuleId.IncomingCall,
|
||||
default: false,
|
||||
enabled: true,
|
||||
actions: [
|
||||
{
|
||||
set_tweak: TweakName.Sound,
|
||||
value: "ring",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
jest.spyOn(document, 'getElementById').mockReturnValue(mockAudioElement);
|
||||
jest.spyOn(document, "getElementById").mockReturnValue(mockAudioElement);
|
||||
|
||||
// silence local notifications by default
|
||||
jest.spyOn(MatrixClientPeg.get(), 'getAccountData').mockImplementation((eventType) => {
|
||||
jest.spyOn(MatrixClientPeg.get(), "getAccountData").mockImplementation((eventType) => {
|
||||
if (eventType.includes(LOCAL_NOTIFICATION_SETTINGS_PREFIX.name)) {
|
||||
return new MatrixEvent({
|
||||
type: eventType,
|
||||
@@ -491,7 +500,7 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should unmute <audio> before playing', () => {
|
||||
it("should unmute <audio> before playing", () => {
|
||||
// Test setup: set the audio element as muted
|
||||
mockAudioElement.muted = true;
|
||||
expect(mockAudioElement.muted).toStrictEqual(true);
|
||||
@@ -504,7 +513,7 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
expect(mockAudioElement.play).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('listens for incoming call events when voip is enabled', () => {
|
||||
it("listens for incoming call events when voip is enabled", () => {
|
||||
const call = new MatrixCall({
|
||||
client: MatrixClientPeg.get(),
|
||||
roomId,
|
||||
@@ -517,9 +526,9 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
expect(callHandler.getCallForRoom(roomId)).toEqual(call);
|
||||
});
|
||||
|
||||
it('rings when incoming call state is ringing and notifications set to ring', () => {
|
||||
it("rings when incoming call state is ringing and notifications set to ring", () => {
|
||||
// remove local notification silencing mock for this test
|
||||
jest.spyOn(MatrixClientPeg.get(), 'getAccountData').mockReturnValue(undefined);
|
||||
jest.spyOn(MatrixClientPeg.get(), "getAccountData").mockReturnValue(undefined);
|
||||
const call = new MatrixCall({
|
||||
client: MatrixClientPeg.get(),
|
||||
roomId,
|
||||
@@ -536,7 +545,7 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
expect(mockAudioElement.play).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not ring when incoming call state is ringing but local notifications are silenced', () => {
|
||||
it("does not ring when incoming call state is ringing but local notifications are silenced", () => {
|
||||
const call = new MatrixCall({
|
||||
client: MatrixClientPeg.get(),
|
||||
roomId,
|
||||
@@ -554,7 +563,7 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
expect(callHandler.isCallSilenced(call.callId)).toEqual(true);
|
||||
});
|
||||
|
||||
it('should force calls to silent when local notifications are silenced', async () => {
|
||||
it("should force calls to silent when local notifications are silenced", async () => {
|
||||
const call = new MatrixCall({
|
||||
client: MatrixClientPeg.get(),
|
||||
roomId,
|
||||
@@ -567,13 +576,13 @@ describe('LegacyCallHandler without third party protocols', () => {
|
||||
expect(callHandler.isCallSilenced(call.callId)).toEqual(true);
|
||||
});
|
||||
|
||||
it('does not unsilence calls when local notifications are silenced', async () => {
|
||||
it("does not unsilence calls when local notifications are silenced", async () => {
|
||||
const call = new MatrixCall({
|
||||
client: MatrixClientPeg.get(),
|
||||
roomId,
|
||||
});
|
||||
const cli = MatrixClientPeg.get();
|
||||
const callHandlerEmitSpy = jest.spyOn(callHandler, 'emit');
|
||||
const callHandlerEmitSpy = jest.spyOn(callHandler, "emit");
|
||||
|
||||
cli.emit(CallEventHandlerEvent.Incoming, call);
|
||||
// reset emit call count
|
||||
|
||||
@@ -40,7 +40,7 @@ describe("Markdown parser test", () => {
|
||||
"https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org",
|
||||
].join("\n");
|
||||
|
||||
it('tests that links with markdown empasis in them are getting properly HTML formatted', () => {
|
||||
it("tests that links with markdown empasis in them are getting properly HTML formatted", () => {
|
||||
/* eslint-disable max-len */
|
||||
const expectedResult = [
|
||||
"<p>Test1:<br />#_foonetic_xkcd:matrix.org<br />http://google.com/_thing_<br />https://matrix.org/_matrix/client/foo/123_<br />#_foonetic_xkcd:matrix.org</p>",
|
||||
@@ -53,7 +53,7 @@ describe("Markdown parser test", () => {
|
||||
const md = new Markdown(testString);
|
||||
expect(md.toHTML()).toEqual(expectedResult);
|
||||
});
|
||||
it('tests that links with autolinks are not touched at all and are still properly formatted', () => {
|
||||
it("tests that links with autolinks are not touched at all and are still properly formatted", () => {
|
||||
const test = [
|
||||
"Test1:",
|
||||
"<#_foonetic_xkcd:matrix.org>",
|
||||
@@ -81,10 +81,10 @@ describe("Markdown parser test", () => {
|
||||
* but it seems to be actually working properly
|
||||
*/
|
||||
const expectedResult = [
|
||||
"<p>Test1:<br /><#_foonetic_xkcd:matrix.org><br /><a href=\"http://google.com/_thing_\">http://google.com/_thing_</a><br /><a href=\"https://matrix.org/_matrix/client/foo/123_\">https://matrix.org/_matrix/client/foo/123_</a><br /><#_foonetic_xkcd:matrix.org></p>",
|
||||
"<p>Test1A:<br /><#_foonetic_xkcd:matrix.org><br /><a href=\"http://google.com/_thing_\">http://google.com/_thing_</a><br /><a href=\"https://matrix.org/_matrix/client/foo/123_\">https://matrix.org/_matrix/client/foo/123_</a><br /><#_foonetic_xkcd:matrix.org></p>",
|
||||
"<p>Test2:<br /><a href=\"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg\">http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg</a><br /><a href=\"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg\">http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg</a></p>",
|
||||
"<p>Test3:<br /><a href=\"https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org\">https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org</a><br /><a href=\"https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org\">https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org</a></p>",
|
||||
'<p>Test1:<br /><#_foonetic_xkcd:matrix.org><br /><a href="http://google.com/_thing_">http://google.com/_thing_</a><br /><a href="https://matrix.org/_matrix/client/foo/123_">https://matrix.org/_matrix/client/foo/123_</a><br /><#_foonetic_xkcd:matrix.org></p>',
|
||||
'<p>Test1A:<br /><#_foonetic_xkcd:matrix.org><br /><a href="http://google.com/_thing_">http://google.com/_thing_</a><br /><a href="https://matrix.org/_matrix/client/foo/123_">https://matrix.org/_matrix/client/foo/123_</a><br /><#_foonetic_xkcd:matrix.org></p>',
|
||||
'<p>Test2:<br /><a href="http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg">http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg</a><br /><a href="http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg">http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg</a></p>',
|
||||
'<p>Test3:<br /><a href="https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org">https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org</a><br /><a href="https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org">https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org</a></p>',
|
||||
"",
|
||||
].join("\n");
|
||||
/* eslint-enable max-len */
|
||||
@@ -92,29 +92,29 @@ describe("Markdown parser test", () => {
|
||||
expect(md.toHTML()).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('expects that links in codeblock are not modified', () => {
|
||||
it("expects that links in codeblock are not modified", () => {
|
||||
const expectedResult = [
|
||||
'<pre><code class="language-Test1:">#_foonetic_xkcd:matrix.org',
|
||||
'http://google.com/_thing_',
|
||||
'https://matrix.org/_matrix/client/foo/123_',
|
||||
'#_foonetic_xkcd:matrix.org',
|
||||
'',
|
||||
'Test1A:',
|
||||
'#_foonetic_xkcd:matrix.org',
|
||||
'http://google.com/_thing_',
|
||||
'https://matrix.org/_matrix/client/foo/123_',
|
||||
'#_foonetic_xkcd:matrix.org',
|
||||
'',
|
||||
'Test2:',
|
||||
'http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg',
|
||||
'http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg',
|
||||
'',
|
||||
'Test3:',
|
||||
'https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org',
|
||||
'https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org```',
|
||||
'</code></pre>',
|
||||
'',
|
||||
].join('\n');
|
||||
"http://google.com/_thing_",
|
||||
"https://matrix.org/_matrix/client/foo/123_",
|
||||
"#_foonetic_xkcd:matrix.org",
|
||||
"",
|
||||
"Test1A:",
|
||||
"#_foonetic_xkcd:matrix.org",
|
||||
"http://google.com/_thing_",
|
||||
"https://matrix.org/_matrix/client/foo/123_",
|
||||
"#_foonetic_xkcd:matrix.org",
|
||||
"",
|
||||
"Test2:",
|
||||
"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg",
|
||||
"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg",
|
||||
"",
|
||||
"Test3:",
|
||||
"https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org",
|
||||
"https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org```",
|
||||
"</code></pre>",
|
||||
"",
|
||||
].join("\n");
|
||||
const md = new Markdown("```" + testString + "```");
|
||||
expect(md.toHTML()).toEqual(expectedResult);
|
||||
});
|
||||
@@ -122,15 +122,19 @@ describe("Markdown parser test", () => {
|
||||
it('expects that links with emphasis are "escaped" correctly', () => {
|
||||
/* eslint-disable max-len */
|
||||
const testString = [
|
||||
'http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg' + " " + 'http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg',
|
||||
'http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg' + " " + 'http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg',
|
||||
"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg" +
|
||||
" " +
|
||||
"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg",
|
||||
"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg" +
|
||||
" " +
|
||||
"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg",
|
||||
"https://example.com/_test_test2_-test3",
|
||||
"https://example.com/_test_test2_test3_",
|
||||
"https://example.com/_test__test2_test3_",
|
||||
"https://example.com/_test__test2__test3_",
|
||||
"https://example.com/_test__test2_test3__",
|
||||
"https://example.com/_test__test2",
|
||||
].join('\n');
|
||||
].join("\n");
|
||||
const expectedResult = [
|
||||
"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg",
|
||||
"http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg",
|
||||
@@ -140,39 +144,47 @@ describe("Markdown parser test", () => {
|
||||
"https://example.com/_test__test2__test3_",
|
||||
"https://example.com/_test__test2_test3__",
|
||||
"https://example.com/_test__test2",
|
||||
].join('<br />');
|
||||
].join("<br />");
|
||||
/* eslint-enable max-len */
|
||||
const md = new Markdown(testString);
|
||||
expect(md.toHTML()).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('expects that the link part will not be accidentally added to <strong>', () => {
|
||||
it("expects that the link part will not be accidentally added to <strong>", () => {
|
||||
/* eslint-disable max-len */
|
||||
const testString = `https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py`;
|
||||
const expectedResult = 'https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py';
|
||||
const expectedResult = "https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py";
|
||||
/* eslint-enable max-len */
|
||||
const md = new Markdown(testString);
|
||||
expect(md.toHTML()).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('expects that the link part will not be accidentally added to <strong> for multiline links', () => {
|
||||
it("expects that the link part will not be accidentally added to <strong> for multiline links", () => {
|
||||
/* eslint-disable max-len */
|
||||
const testString = [
|
||||
'https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py' + " " + 'https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py',
|
||||
'https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py' + " " + 'https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py',
|
||||
].join('\n');
|
||||
"https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py" +
|
||||
" " +
|
||||
"https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py",
|
||||
"https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py" +
|
||||
" " +
|
||||
"https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py",
|
||||
].join("\n");
|
||||
const expectedResult = [
|
||||
'https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py' + " " + 'https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py',
|
||||
'https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py' + " " + 'https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py',
|
||||
].join('<br />');
|
||||
"https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py" +
|
||||
" " +
|
||||
"https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py",
|
||||
"https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py" +
|
||||
" " +
|
||||
"https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py",
|
||||
].join("<br />");
|
||||
/* eslint-enable max-len */
|
||||
const md = new Markdown(testString);
|
||||
expect(md.toHTML()).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('resumes applying formatting to the rest of a message after a link', () => {
|
||||
const testString = 'http://google.com/_thing_ *does* __not__ exist';
|
||||
const expectedResult = 'http://google.com/_thing_ <em>does</em> <strong>not</strong> exist';
|
||||
it("resumes applying formatting to the rest of a message after a link", () => {
|
||||
const testString = "http://google.com/_thing_ *does* __not__ exist";
|
||||
const expectedResult = "http://google.com/_thing_ <em>does</em> <strong>not</strong> exist";
|
||||
const md = new Markdown(testString);
|
||||
expect(md.toHTML()).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
@@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { mocked } from 'jest-mock';
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { SettingLevel } from "../src/settings/SettingLevel";
|
||||
import { MatrixClientPeg } from '../src/MatrixClientPeg';
|
||||
import { MatrixClientPeg } from "../src/MatrixClientPeg";
|
||||
import { stubClient } from "./test-utils";
|
||||
import MediaDeviceHandler from "../src/MediaDeviceHandler";
|
||||
import SettingsStore from '../src/settings/SettingsStore';
|
||||
import SettingsStore from "../src/settings/SettingsStore";
|
||||
|
||||
jest.mock("../src/settings/SettingsStore");
|
||||
|
||||
@@ -51,9 +51,7 @@ describe("MediaDeviceHandler", () => {
|
||||
await MediaDeviceHandler.setAudioNoiseSuppression(false);
|
||||
|
||||
expectedAudioSettings.forEach((value, key) => {
|
||||
expect(SettingsStoreMock.setValue).toHaveBeenCalledWith(
|
||||
key, null, SettingLevel.DEVICE, value,
|
||||
);
|
||||
expect(SettingsStoreMock.setValue).toHaveBeenCalledWith(key, null, SettingLevel.DEVICE, value);
|
||||
});
|
||||
|
||||
expect(MatrixClientPeg.get().getMediaHandler().setAudioSettings).toHaveBeenCalledWith({
|
||||
|
||||
@@ -78,12 +78,14 @@ describe("Notifier", () => {
|
||||
mockClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
getAccountData: jest.fn().mockImplementation(eventType => accountDataStore[eventType]),
|
||||
getAccountData: jest.fn().mockImplementation((eventType) => accountDataStore[eventType]),
|
||||
setAccountData: jest.fn().mockImplementation((eventType, content) => {
|
||||
accountDataStore[eventType] = content ? new MatrixEvent({
|
||||
type: eventType,
|
||||
content,
|
||||
}) : undefined;
|
||||
accountDataStore[eventType] = content
|
||||
? new MatrixEvent({
|
||||
type: eventType,
|
||||
content,
|
||||
})
|
||||
: undefined;
|
||||
}),
|
||||
decryptEventIfNeeded: jest.fn(),
|
||||
getRoom: jest.fn(),
|
||||
@@ -107,20 +109,20 @@ describe("Notifier", () => {
|
||||
|
||||
Notifier.isBodyEnabled = jest.fn().mockReturnValue(true);
|
||||
|
||||
mockClient.getRoom.mockImplementation(id => {
|
||||
mockClient.getRoom.mockImplementation((id) => {
|
||||
return id === roomId ? testRoom : new Room(id, mockClient, mockClient.getUserId());
|
||||
});
|
||||
});
|
||||
|
||||
describe('triggering notification from events', () => {
|
||||
describe("triggering notification from events", () => {
|
||||
let hasStartedNotiferBefore = false;
|
||||
|
||||
const event = new MatrixEvent({
|
||||
sender: '@alice:server.org',
|
||||
type: 'm.room.message',
|
||||
room_id: '!room:server.org',
|
||||
sender: "@alice:server.org",
|
||||
type: "m.room.message",
|
||||
room_id: "!room:server.org",
|
||||
content: {
|
||||
body: 'hey',
|
||||
body: "hey",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -142,28 +144,28 @@ describe("Notifier", () => {
|
||||
});
|
||||
|
||||
mockSettings = {
|
||||
'notificationsEnabled': true,
|
||||
'audioNotificationsEnabled': true,
|
||||
notificationsEnabled: true,
|
||||
audioNotificationsEnabled: true,
|
||||
};
|
||||
|
||||
// enable notifications by default
|
||||
jest.spyOn(SettingsStore, "getValue").mockReset().mockImplementation(
|
||||
settingName => mockSettings[settingName] ?? false,
|
||||
);
|
||||
jest.spyOn(SettingsStore, "getValue")
|
||||
.mockReset()
|
||||
.mockImplementation((settingName) => mockSettings[settingName] ?? false);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Notifier.stop();
|
||||
});
|
||||
|
||||
it('does not create notifications before syncing has started', () => {
|
||||
it("does not create notifications before syncing has started", () => {
|
||||
emitLiveEvent(event);
|
||||
|
||||
expect(MockPlatform.displayNotification).not.toHaveBeenCalled();
|
||||
expect(MockPlatform.loudNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create notifications for own event', () => {
|
||||
it("does not create notifications for own event", () => {
|
||||
const ownEvent = new MatrixEvent({ sender: userId });
|
||||
|
||||
mockClient!.emit(ClientEvent.Sync, SyncState.Syncing, null);
|
||||
@@ -173,7 +175,7 @@ describe("Notifier", () => {
|
||||
expect(MockPlatform.loudNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create notifications for non-live events (scrollback)', () => {
|
||||
it("does not create notifications for non-live events (scrollback)", () => {
|
||||
mockClient!.emit(ClientEvent.Sync, SyncState.Syncing, null);
|
||||
mockClient!.emit(RoomEvent.Timeline, event, testRoom, false, false, {
|
||||
liveEvent: false,
|
||||
@@ -184,7 +186,7 @@ describe("Notifier", () => {
|
||||
expect(MockPlatform.loudNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create notifications for rooms which cannot be obtained via client.getRoom', () => {
|
||||
it("does not create notifications for rooms which cannot be obtained via client.getRoom", () => {
|
||||
mockClient!.emit(ClientEvent.Sync, SyncState.Syncing, null);
|
||||
mockClient.getRoom.mockReturnValue(null);
|
||||
mockClient!.emit(RoomEvent.Timeline, event, testRoom, false, false, {
|
||||
@@ -196,7 +198,7 @@ describe("Notifier", () => {
|
||||
expect(MockPlatform.loudNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create notifications when event does not have notify push action', () => {
|
||||
it("does not create notifications when event does not have notify push action", () => {
|
||||
mockClient.getPushActionsForEvent.mockReturnValue({
|
||||
notify: false,
|
||||
tweaks: {
|
||||
@@ -211,29 +213,21 @@ describe("Notifier", () => {
|
||||
expect(MockPlatform.loudNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates desktop notification when enabled', () => {
|
||||
it("creates desktop notification when enabled", () => {
|
||||
mockClient!.emit(ClientEvent.Sync, SyncState.Syncing, null);
|
||||
emitLiveEvent(event);
|
||||
|
||||
expect(MockPlatform.displayNotification).toHaveBeenCalledWith(
|
||||
testRoom.name,
|
||||
'hey',
|
||||
null,
|
||||
testRoom,
|
||||
event,
|
||||
);
|
||||
expect(MockPlatform.displayNotification).toHaveBeenCalledWith(testRoom.name, "hey", null, testRoom, event);
|
||||
});
|
||||
|
||||
it('creates a loud notification when enabled', () => {
|
||||
it("creates a loud notification when enabled", () => {
|
||||
mockClient!.emit(ClientEvent.Sync, SyncState.Syncing, null);
|
||||
emitLiveEvent(event);
|
||||
|
||||
expect(MockPlatform.loudNotification).toHaveBeenCalledWith(
|
||||
event, testRoom,
|
||||
);
|
||||
expect(MockPlatform.loudNotification).toHaveBeenCalledWith(event, testRoom);
|
||||
});
|
||||
|
||||
it('does not create loud notification when event does not have sound tweak in push actions', () => {
|
||||
it("does not create loud notification when event does not have sound tweak in push actions", () => {
|
||||
mockClient.getPushActionsForEvent.mockReturnValue({
|
||||
notify: true,
|
||||
tweaks: {
|
||||
@@ -252,7 +246,7 @@ describe("Notifier", () => {
|
||||
});
|
||||
|
||||
describe("_displayPopupNotification", () => {
|
||||
const testCases: {event: IContent | undefined, count: number}[] = [
|
||||
const testCases: { event: IContent | undefined; count: number }[] = [
|
||||
{ event: { is_silenced: true }, count: 0 },
|
||||
{ event: { is_silenced: false }, count: 1 },
|
||||
{ event: undefined, count: 1 },
|
||||
@@ -274,7 +268,7 @@ describe("Notifier", () => {
|
||||
});
|
||||
|
||||
describe("_playAudioNotification", () => {
|
||||
const testCases: {event: IContent | undefined, count: number}[] = [
|
||||
const testCases: { event: IContent | undefined; count: number }[] = [
|
||||
{ event: { is_silenced: true }, count: 0 },
|
||||
{ event: { is_silenced: false }, count: 1 },
|
||||
{ event: undefined, count: 1 },
|
||||
@@ -330,13 +324,15 @@ describe("Notifier", () => {
|
||||
|
||||
const callEvent = callOnEvent();
|
||||
|
||||
expect(ToastStore.sharedInstance().addOrReplaceToast).toHaveBeenCalledWith(expect.objectContaining({
|
||||
key: `call_${callEvent.getStateKey()}`,
|
||||
priority: 100,
|
||||
component: IncomingCallToast,
|
||||
bodyClassName: "mx_IncomingCallToast",
|
||||
props: { callEvent },
|
||||
}));
|
||||
expect(ToastStore.sharedInstance().addOrReplaceToast).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
key: `call_${callEvent.getStateKey()}`,
|
||||
priority: 100,
|
||||
component: IncomingCallToast,
|
||||
bodyClassName: "mx_IncomingCallToast",
|
||||
props: { callEvent },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should not show toast when group calls are not supported", () => {
|
||||
@@ -356,7 +352,7 @@ describe("Notifier", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('local notification settings', () => {
|
||||
describe("local notification settings", () => {
|
||||
const createLocalNotificationSettingsIfNeededMock = mocked(createLocalNotificationSettingsIfNeeded);
|
||||
let hasStartedNotiferBefore = false;
|
||||
beforeEach(() => {
|
||||
@@ -375,36 +371,34 @@ describe("Notifier", () => {
|
||||
Notifier.stop();
|
||||
});
|
||||
|
||||
it('does not create local notifications event after a sync error', () => {
|
||||
it("does not create local notifications event after a sync error", () => {
|
||||
mockClient.emit(ClientEvent.Sync, SyncState.Error, SyncState.Syncing);
|
||||
expect(createLocalNotificationSettingsIfNeededMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create local notifications event after sync stops', () => {
|
||||
it("does not create local notifications event after sync stops", () => {
|
||||
mockClient.emit(ClientEvent.Sync, SyncState.Stopped, SyncState.Syncing);
|
||||
expect(createLocalNotificationSettingsIfNeededMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create local notifications event after a cached sync', () => {
|
||||
it("does not create local notifications event after a cached sync", () => {
|
||||
mockClient.emit(ClientEvent.Sync, SyncState.Syncing, SyncState.Syncing, {
|
||||
fromCache: true,
|
||||
});
|
||||
expect(createLocalNotificationSettingsIfNeededMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates local notifications event after a non-cached sync', () => {
|
||||
it("creates local notifications event after a non-cached sync", () => {
|
||||
mockClient.emit(ClientEvent.Sync, SyncState.Syncing, SyncState.Syncing, {});
|
||||
expect(createLocalNotificationSettingsIfNeededMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_evaluateEvent', () => {
|
||||
describe("_evaluateEvent", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId")
|
||||
.mockReturnValue(testRoom.roomId);
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue(testRoom.roomId);
|
||||
|
||||
jest.spyOn(UserActivity.sharedInstance(), "userActiveRecently")
|
||||
.mockReturnValue(true);
|
||||
jest.spyOn(UserActivity.sharedInstance(), "userActiveRecently").mockReturnValue(true);
|
||||
|
||||
jest.spyOn(Modal, "hasDialogs").mockReturnValue(false);
|
||||
|
||||
@@ -457,9 +451,7 @@ describe("Notifier", () => {
|
||||
thread_id: rootEvent.getId(),
|
||||
});
|
||||
|
||||
await waitFor(() =>
|
||||
expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId()),
|
||||
);
|
||||
await waitFor(() => expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId()));
|
||||
|
||||
Notifier._evaluateEvent(events[1]);
|
||||
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -14,25 +14,26 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { mocked } from 'jest-mock';
|
||||
import { PostHog } from 'posthog-js';
|
||||
import { mocked } from "jest-mock";
|
||||
import { PostHog } from "posthog-js";
|
||||
|
||||
import { Anonymity, getRedactedCurrentLocation, IPosthogEvent, PosthogAnalytics } from '../src/PosthogAnalytics';
|
||||
import SdkConfig from '../src/SdkConfig';
|
||||
import { getMockClientWithEventEmitter } from './test-utils';
|
||||
import { Anonymity, getRedactedCurrentLocation, IPosthogEvent, PosthogAnalytics } from "../src/PosthogAnalytics";
|
||||
import SdkConfig from "../src/SdkConfig";
|
||||
import { getMockClientWithEventEmitter } from "./test-utils";
|
||||
import SettingsStore from "../src/settings/SettingsStore";
|
||||
import { Layout } from "../src/settings/enums/Layout";
|
||||
import defaultDispatcher from "../src/dispatcher/dispatcher";
|
||||
import { Action } from "../src/dispatcher/actions";
|
||||
import { SettingLevel } from "../src/settings/SettingLevel";
|
||||
|
||||
const getFakePosthog = (): PostHog => ({
|
||||
capture: jest.fn(),
|
||||
init: jest.fn(),
|
||||
identify: jest.fn(),
|
||||
reset: jest.fn(),
|
||||
register: jest.fn(),
|
||||
} as unknown as PostHog);
|
||||
const getFakePosthog = (): PostHog =>
|
||||
({
|
||||
capture: jest.fn(),
|
||||
init: jest.fn(),
|
||||
identify: jest.fn(),
|
||||
reset: jest.fn(),
|
||||
register: jest.fn(),
|
||||
} as unknown as PostHog);
|
||||
|
||||
export interface ITestEvent extends IPosthogEvent {
|
||||
eventName: "JestTestEvents";
|
||||
@@ -198,58 +199,70 @@ describe("PosthogAnalytics", () => {
|
||||
|
||||
it("should send layout IRC correctly", async () => {
|
||||
await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.SettingUpdated,
|
||||
settingName: "layout",
|
||||
}, true);
|
||||
defaultDispatcher.dispatch(
|
||||
{
|
||||
action: Action.SettingUpdated,
|
||||
settingName: "layout",
|
||||
},
|
||||
true,
|
||||
);
|
||||
analytics.trackEvent<ITestEvent>({
|
||||
eventName: "JestTestEvents",
|
||||
});
|
||||
expect(mocked(fakePosthog).capture.mock.calls[0][1]["$set"]).toStrictEqual({
|
||||
"WebLayout": "IRC",
|
||||
WebLayout: "IRC",
|
||||
});
|
||||
});
|
||||
|
||||
it("should send layout Bubble correctly", async () => {
|
||||
await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.SettingUpdated,
|
||||
settingName: "layout",
|
||||
}, true);
|
||||
defaultDispatcher.dispatch(
|
||||
{
|
||||
action: Action.SettingUpdated,
|
||||
settingName: "layout",
|
||||
},
|
||||
true,
|
||||
);
|
||||
analytics.trackEvent<ITestEvent>({
|
||||
eventName: "JestTestEvents",
|
||||
});
|
||||
expect(mocked(fakePosthog).capture.mock.calls[0][1]["$set"]).toStrictEqual({
|
||||
"WebLayout": "Bubble",
|
||||
WebLayout: "Bubble",
|
||||
});
|
||||
});
|
||||
|
||||
it("should send layout Group correctly", async () => {
|
||||
await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.SettingUpdated,
|
||||
settingName: "layout",
|
||||
}, true);
|
||||
defaultDispatcher.dispatch(
|
||||
{
|
||||
action: Action.SettingUpdated,
|
||||
settingName: "layout",
|
||||
},
|
||||
true,
|
||||
);
|
||||
analytics.trackEvent<ITestEvent>({
|
||||
eventName: "JestTestEvents",
|
||||
});
|
||||
expect(mocked(fakePosthog).capture.mock.calls[0][1]["$set"]).toStrictEqual({
|
||||
"WebLayout": "Group",
|
||||
WebLayout: "Group",
|
||||
});
|
||||
});
|
||||
|
||||
it("should send layout Compact correctly", async () => {
|
||||
await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
|
||||
await SettingsStore.setValue("useCompactLayout", null, SettingLevel.DEVICE, true);
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.SettingUpdated,
|
||||
settingName: "useCompactLayout",
|
||||
}, true);
|
||||
defaultDispatcher.dispatch(
|
||||
{
|
||||
action: Action.SettingUpdated,
|
||||
settingName: "useCompactLayout",
|
||||
},
|
||||
true,
|
||||
);
|
||||
analytics.trackEvent<ITestEvent>({
|
||||
eventName: "JestTestEvents",
|
||||
});
|
||||
expect(mocked(fakePosthog).capture.mock.calls[0][1]["$set"]).toStrictEqual({
|
||||
"WebLayout": "Compact",
|
||||
WebLayout: "Compact",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,7 +84,7 @@ describe("Reply", () => {
|
||||
content: {
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$event1",
|
||||
event_id: "$event1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -96,24 +96,30 @@ describe("Reply", () => {
|
||||
|
||||
describe("stripPlainReply", () => {
|
||||
it("Removes leading quotes until the first blank line", () => {
|
||||
expect(stripPlainReply(`
|
||||
expect(
|
||||
stripPlainReply(
|
||||
`
|
||||
> This is part
|
||||
> of the quote
|
||||
|
||||
But this is not
|
||||
`.trim())).toBe("But this is not");
|
||||
`.trim(),
|
||||
),
|
||||
).toBe("But this is not");
|
||||
});
|
||||
});
|
||||
|
||||
describe("stripHTMLReply", () => {
|
||||
it("Removes <mx-reply> from the input", () => {
|
||||
expect(stripHTMLReply(`
|
||||
expect(
|
||||
stripHTMLReply(`
|
||||
<mx-reply>
|
||||
This is part
|
||||
of the quote
|
||||
</mx-reply>
|
||||
But this is not
|
||||
`).trim()).toBe("But this is not");
|
||||
`).trim(),
|
||||
).toBe("But this is not");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -14,17 +14,13 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { mocked } from 'jest-mock';
|
||||
import { mocked } from "jest-mock";
|
||||
import { ConditionKind, PushRuleActionName, TweakName } from "matrix-js-sdk/src/@types/PushRules";
|
||||
import { NotificationCountType, Room } from 'matrix-js-sdk/src/models/room';
|
||||
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
|
||||
|
||||
import { mkEvent, stubClient } from "./test-utils";
|
||||
import { MatrixClientPeg } from "../src/MatrixClientPeg";
|
||||
import {
|
||||
getRoomNotifsState,
|
||||
RoomNotifState,
|
||||
getUnreadNotificationCount,
|
||||
} from "../src/RoomNotifs";
|
||||
import { getRoomNotifsState, RoomNotifState, getUnreadNotificationCount } from "../src/RoomNotifs";
|
||||
|
||||
describe("RoomNotifs test", () => {
|
||||
beforeEach(() => {
|
||||
@@ -35,12 +31,14 @@ describe("RoomNotifs test", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
mocked(cli).pushRules = {
|
||||
global: {
|
||||
override: [{
|
||||
rule_id: "!roomId:server",
|
||||
enabled: true,
|
||||
default: false,
|
||||
actions: [],
|
||||
}],
|
||||
override: [
|
||||
{
|
||||
rule_id: "!roomId:server",
|
||||
enabled: true,
|
||||
default: false,
|
||||
actions: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
expect(getRoomNotifsState(cli, "!roomId:server")).toBe(null);
|
||||
@@ -56,17 +54,21 @@ describe("RoomNotifs test", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
cli.pushRules = {
|
||||
global: {
|
||||
override: [{
|
||||
rule_id: "!roomId:server",
|
||||
enabled: true,
|
||||
default: false,
|
||||
conditions: [{
|
||||
kind: ConditionKind.EventMatch,
|
||||
key: "room_id",
|
||||
pattern: "!roomId:server",
|
||||
}],
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
}],
|
||||
override: [
|
||||
{
|
||||
rule_id: "!roomId:server",
|
||||
enabled: true,
|
||||
default: false,
|
||||
conditions: [
|
||||
{
|
||||
kind: ConditionKind.EventMatch,
|
||||
key: "room_id",
|
||||
pattern: "!roomId:server",
|
||||
},
|
||||
],
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
expect(getRoomNotifsState(cli, "!roomId:server")).toBe(RoomNotifState.Mute);
|
||||
|
||||
@@ -17,14 +17,14 @@ limitations under the License.
|
||||
import { mocked } from "jest-mock";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import ScalarAuthClient from '../src/ScalarAuthClient';
|
||||
import { stubClient } from './test-utils';
|
||||
import ScalarAuthClient from "../src/ScalarAuthClient";
|
||||
import { stubClient } from "./test-utils";
|
||||
import SdkConfig from "../src/SdkConfig";
|
||||
import { WidgetType } from "../src/widgets/WidgetType";
|
||||
|
||||
describe('ScalarAuthClient', function() {
|
||||
const apiUrl = 'https://test.com/api';
|
||||
const uiUrl = 'https:/test.com/app';
|
||||
describe("ScalarAuthClient", function () {
|
||||
const apiUrl = "https://test.com/api";
|
||||
const uiUrl = "https:/test.com/app";
|
||||
const tokenObject = {
|
||||
access_token: "token",
|
||||
token_type: "Bearer",
|
||||
@@ -33,12 +33,12 @@ describe('ScalarAuthClient', function() {
|
||||
};
|
||||
|
||||
let client;
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
jest.clearAllMocks();
|
||||
client = stubClient();
|
||||
});
|
||||
|
||||
it('should request a new token if the old one fails', async function() {
|
||||
it("should request a new token if the old one fails", async function () {
|
||||
const sac = new ScalarAuthClient(apiUrl + 0, uiUrl);
|
||||
|
||||
fetchMock.get("https://test.com/api0/account?scalar_token=brokentoken&v=1.1", {
|
||||
@@ -60,7 +60,7 @@ describe('ScalarAuthClient', function() {
|
||||
expect(sac.exchangeForScalarToken).toBeCalledWith(tokenObject);
|
||||
expect(sac.hasCredentials).toBeTruthy();
|
||||
// @ts-ignore private property
|
||||
expect(sac.scalarToken).toEqual('wokentoken');
|
||||
expect(sac.scalarToken).toEqual("wokentoken");
|
||||
});
|
||||
|
||||
describe("exchangeForScalarToken", () => {
|
||||
@@ -191,22 +191,29 @@ describe('ScalarAuthClient', function() {
|
||||
});
|
||||
|
||||
it("should send state=disable to API /widgets/set_assets_state", async () => {
|
||||
fetchMock.get("https://test.com/api8/widgets/set_assets_state?scalar_token=wokentoken1" +
|
||||
"&widget_type=m.custom&widget_id=id1&state=disable", {
|
||||
body: "OK",
|
||||
});
|
||||
fetchMock.get(
|
||||
"https://test.com/api8/widgets/set_assets_state?scalar_token=wokentoken1" +
|
||||
"&widget_type=m.custom&widget_id=id1&state=disable",
|
||||
{
|
||||
body: "OK",
|
||||
},
|
||||
);
|
||||
|
||||
await expect(sac.disableWidgetAssets(WidgetType.CUSTOM, "id1")).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should throw upon non-20x code", async () => {
|
||||
fetchMock.get("https://test.com/api8/widgets/set_assets_state?scalar_token=wokentoken1" +
|
||||
"&widget_type=m.custom&widget_id=id2&state=disable", {
|
||||
status: 500,
|
||||
});
|
||||
fetchMock.get(
|
||||
"https://test.com/api8/widgets/set_assets_state?scalar_token=wokentoken1" +
|
||||
"&widget_type=m.custom&widget_id=id2&state=disable",
|
||||
{
|
||||
status: 500,
|
||||
},
|
||||
);
|
||||
|
||||
await expect(sac.disableWidgetAssets(WidgetType.CUSTOM, "id2"))
|
||||
.rejects.toThrow("Scalar request failed: 500");
|
||||
await expect(sac.disableWidgetAssets(WidgetType.CUSTOM, "id2")).rejects.toThrow(
|
||||
"Scalar request failed: 500",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixClient, Room } from 'matrix-js-sdk/src/matrix';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { Command, Commands, getCommand } from '../src/SlashCommands';
|
||||
import { createTestClient } from './test-utils';
|
||||
import { MatrixClientPeg } from '../src/MatrixClientPeg';
|
||||
import { LocalRoom, LOCAL_ROOM_ID_PREFIX } from '../src/models/LocalRoom';
|
||||
import SettingsStore from '../src/settings/SettingsStore';
|
||||
import LegacyCallHandler from '../src/LegacyCallHandler';
|
||||
import { SdkContextClass } from '../src/contexts/SDKContext';
|
||||
import { Command, Commands, getCommand } from "../src/SlashCommands";
|
||||
import { createTestClient } from "./test-utils";
|
||||
import { MatrixClientPeg } from "../src/MatrixClientPeg";
|
||||
import { LocalRoom, LOCAL_ROOM_ID_PREFIX } from "../src/models/LocalRoom";
|
||||
import SettingsStore from "../src/settings/SettingsStore";
|
||||
import LegacyCallHandler from "../src/LegacyCallHandler";
|
||||
import { SdkContextClass } from "../src/contexts/SDKContext";
|
||||
|
||||
describe('SlashCommands', () => {
|
||||
describe("SlashCommands", () => {
|
||||
let client: MatrixClient;
|
||||
const roomId = "!room:example.com";
|
||||
let room: Room;
|
||||
@@ -55,7 +55,7 @@ describe('SlashCommands', () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
client = createTestClient();
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(client);
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(client);
|
||||
|
||||
room = new Room(roomId, client, client.getUserId());
|
||||
localRoom = new LocalRoom(localRoomId, client, client.getUserId());
|
||||
@@ -63,8 +63,8 @@ describe('SlashCommands', () => {
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId");
|
||||
});
|
||||
|
||||
describe('/topic', () => {
|
||||
it('sets topic', async () => {
|
||||
describe("/topic", () => {
|
||||
it("sets topic", async () => {
|
||||
const command = getCommand("/topic pizza");
|
||||
expect(command.cmd).toBeDefined();
|
||||
expect(command.args).toBeDefined();
|
||||
@@ -226,10 +226,7 @@ describe('SlashCommands', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe.each([
|
||||
"rainbow",
|
||||
"rainbowme",
|
||||
])("/%s", (commandName: string) => {
|
||||
describe.each(["rainbow", "rainbowme"])("/%s", (commandName: string) => {
|
||||
const command = findCommand(commandName);
|
||||
|
||||
it("should return usage if no args", () => {
|
||||
|
||||
@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { SlidingSync } from 'matrix-js-sdk/src/sliding-sync';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { MatrixClient, MatrixEvent, Room } from 'matrix-js-sdk/src/matrix';
|
||||
import { SlidingSync } from "matrix-js-sdk/src/sliding-sync";
|
||||
import { mocked } from "jest-mock";
|
||||
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { SlidingSyncManager } from '../src/SlidingSyncManager';
|
||||
import { stubClient } from './test-utils';
|
||||
import { SlidingSyncManager } from "../src/SlidingSyncManager";
|
||||
import { stubClient } from "./test-utils";
|
||||
|
||||
jest.mock('matrix-js-sdk/src/sliding-sync');
|
||||
const MockSlidingSync = <jest.Mock<SlidingSync>><unknown>SlidingSync;
|
||||
jest.mock("matrix-js-sdk/src/sliding-sync");
|
||||
const MockSlidingSync = <jest.Mock<SlidingSync>>(<unknown>SlidingSync);
|
||||
|
||||
describe('SlidingSyncManager', () => {
|
||||
describe("SlidingSyncManager", () => {
|
||||
let manager: SlidingSyncManager;
|
||||
let slidingSync: SlidingSync;
|
||||
let client: MatrixClient;
|
||||
@@ -93,24 +93,29 @@ describe('SlidingSyncManager', () => {
|
||||
await manager.startSpidering(batchSize, gapMs);
|
||||
// we expect calls for 10,19 -> 20,29 -> 30,39 -> 40,49 -> 50,59 -> 60,69
|
||||
const wantWindows = [
|
||||
[10, 19], [20, 29], [30, 39], [40, 49], [50, 59], [60, 69],
|
||||
[10, 19],
|
||||
[20, 29],
|
||||
[30, 39],
|
||||
[40, 49],
|
||||
[50, 59],
|
||||
[60, 69],
|
||||
];
|
||||
expect(slidingSync.getListData).toBeCalledTimes(wantWindows.length);
|
||||
expect(slidingSync.setList).toBeCalledTimes(1);
|
||||
expect(slidingSync.setListRanges).toBeCalledTimes(wantWindows.length-1);
|
||||
expect(slidingSync.setListRanges).toBeCalledTimes(wantWindows.length - 1);
|
||||
wantWindows.forEach((range, i) => {
|
||||
if (i === 0) {
|
||||
expect(slidingSync.setList).toBeCalledWith(
|
||||
manager.getOrAllocateListIndex(SlidingSyncManager.ListSearch),
|
||||
expect.objectContaining({
|
||||
ranges: [[0, batchSize-1], range],
|
||||
ranges: [[0, batchSize - 1], range],
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
expect(slidingSync.setListRanges).toBeCalledWith(
|
||||
manager.getOrAllocateListIndex(SlidingSyncManager.ListSearch),
|
||||
[[0, batchSize-1], range],
|
||||
[[0, batchSize - 1], range],
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -130,7 +135,10 @@ describe('SlidingSyncManager', () => {
|
||||
expect(slidingSync.setList).toBeCalledWith(
|
||||
manager.getOrAllocateListIndex(SlidingSyncManager.ListSearch),
|
||||
expect.objectContaining({
|
||||
ranges: [[0, batchSize-1], [batchSize, batchSize+batchSize-1]],
|
||||
ranges: [
|
||||
[0, batchSize - 1],
|
||||
[batchSize, batchSize + batchSize - 1],
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -150,7 +158,10 @@ describe('SlidingSyncManager', () => {
|
||||
expect(slidingSync.setList).toBeCalledWith(
|
||||
manager.getOrAllocateListIndex(SlidingSyncManager.ListSearch),
|
||||
expect.objectContaining({
|
||||
ranges: [[0, batchSize-1], [batchSize, batchSize+batchSize-1]],
|
||||
ranges: [
|
||||
[0, batchSize - 1],
|
||||
[batchSize, batchSize + batchSize - 1],
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -14,15 +14,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
MatrixEvent,
|
||||
EventType,
|
||||
SERVICE_TYPES,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
import { MatrixEvent, EventType, SERVICE_TYPES } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { startTermsFlow, Service } from '../src/Terms';
|
||||
import { getMockClientWithEventEmitter } from './test-utils';
|
||||
import { MatrixClientPeg } from '../src/MatrixClientPeg';
|
||||
import { startTermsFlow, Service } from "../src/Terms";
|
||||
import { getMockClientWithEventEmitter } from "./test-utils";
|
||||
import { MatrixClientPeg } from "../src/MatrixClientPeg";
|
||||
|
||||
const POLICY_ONE = {
|
||||
version: "six",
|
||||
@@ -40,10 +36,10 @@ const POLICY_TWO = {
|
||||
},
|
||||
};
|
||||
|
||||
const IM_SERVICE_ONE = new Service(SERVICE_TYPES.IM, 'https://imone.test', 'a token token');
|
||||
const IM_SERVICE_TWO = new Service(SERVICE_TYPES.IM, 'https://imtwo.test', 'a token token');
|
||||
const IM_SERVICE_ONE = new Service(SERVICE_TYPES.IM, "https://imone.test", "a token token");
|
||||
const IM_SERVICE_TWO = new Service(SERVICE_TYPES.IM, "https://imtwo.test", "a token token");
|
||||
|
||||
describe('Terms', function() {
|
||||
describe("Terms", function () {
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getAccountData: jest.fn(),
|
||||
getTerms: jest.fn(),
|
||||
@@ -51,7 +47,7 @@ describe('Terms', function() {
|
||||
setAccountData: jest.fn(),
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
jest.clearAllMocks();
|
||||
mockClient.getAccountData.mockReturnValue(null);
|
||||
mockClient.getTerms.mockResolvedValue(null);
|
||||
@@ -59,30 +55,33 @@ describe('Terms', function() {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockRestore();
|
||||
jest.spyOn(MatrixClientPeg, "get").mockRestore();
|
||||
});
|
||||
|
||||
it('should prompt for all terms & services if no account data', async function() {
|
||||
it("should prompt for all terms & services if no account data", async function () {
|
||||
mockClient.getAccountData.mockReturnValue(null);
|
||||
mockClient.getTerms.mockResolvedValue({
|
||||
policies: {
|
||||
"policy_the_first": POLICY_ONE,
|
||||
policy_the_first: POLICY_ONE,
|
||||
},
|
||||
});
|
||||
const interactionCallback = jest.fn().mockResolvedValue([]);
|
||||
await startTermsFlow([IM_SERVICE_ONE], interactionCallback);
|
||||
|
||||
expect(interactionCallback).toBeCalledWith([
|
||||
{
|
||||
service: IM_SERVICE_ONE,
|
||||
policies: {
|
||||
policy_the_first: POLICY_ONE,
|
||||
expect(interactionCallback).toBeCalledWith(
|
||||
[
|
||||
{
|
||||
service: IM_SERVICE_ONE,
|
||||
policies: {
|
||||
policy_the_first: POLICY_ONE,
|
||||
},
|
||||
},
|
||||
},
|
||||
], []);
|
||||
],
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
it('should not prompt if all policies are signed in account data', async function() {
|
||||
it("should not prompt if all policies are signed in account data", async function () {
|
||||
const directEvent = new MatrixEvent({
|
||||
type: EventType.Direct,
|
||||
content: {
|
||||
@@ -92,7 +91,7 @@ describe('Terms', function() {
|
||||
mockClient.getAccountData.mockReturnValue(directEvent);
|
||||
mockClient.getTerms.mockResolvedValue({
|
||||
policies: {
|
||||
"policy_the_first": POLICY_ONE,
|
||||
policy_the_first: POLICY_ONE,
|
||||
},
|
||||
});
|
||||
mockClient.agreeToTerms;
|
||||
@@ -101,15 +100,12 @@ describe('Terms', function() {
|
||||
await startTermsFlow([IM_SERVICE_ONE], interactionCallback);
|
||||
|
||||
expect(interactionCallback).not.toHaveBeenCalled();
|
||||
expect(mockClient.agreeToTerms).toBeCalledWith(
|
||||
SERVICE_TYPES.IM,
|
||||
'https://imone.test',
|
||||
'a token token',
|
||||
["http://example.com/one"],
|
||||
);
|
||||
expect(mockClient.agreeToTerms).toBeCalledWith(SERVICE_TYPES.IM, "https://imone.test", "a token token", [
|
||||
"http://example.com/one",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should prompt for only terms that aren't already signed", async function() {
|
||||
it("should prompt for only terms that aren't already signed", async function () {
|
||||
const directEvent = new MatrixEvent({
|
||||
type: EventType.Direct,
|
||||
content: {
|
||||
@@ -120,31 +116,32 @@ describe('Terms', function() {
|
||||
|
||||
mockClient.getTerms.mockResolvedValue({
|
||||
policies: {
|
||||
"policy_the_first": POLICY_ONE,
|
||||
"policy_the_second": POLICY_TWO,
|
||||
policy_the_first: POLICY_ONE,
|
||||
policy_the_second: POLICY_TWO,
|
||||
},
|
||||
});
|
||||
|
||||
const interactionCallback = jest.fn().mockResolvedValue(["http://example.com/one", "http://example.com/two"]);
|
||||
await startTermsFlow([IM_SERVICE_ONE], interactionCallback);
|
||||
|
||||
expect(interactionCallback).toBeCalledWith([
|
||||
{
|
||||
service: IM_SERVICE_ONE,
|
||||
policies: {
|
||||
policy_the_second: POLICY_TWO,
|
||||
expect(interactionCallback).toBeCalledWith(
|
||||
[
|
||||
{
|
||||
service: IM_SERVICE_ONE,
|
||||
policies: {
|
||||
policy_the_second: POLICY_TWO,
|
||||
},
|
||||
},
|
||||
},
|
||||
], ["http://example.com/one"]);
|
||||
expect(mockClient.agreeToTerms).toBeCalledWith(
|
||||
SERVICE_TYPES.IM,
|
||||
'https://imone.test',
|
||||
'a token token',
|
||||
["http://example.com/one", "http://example.com/two"],
|
||||
],
|
||||
["http://example.com/one"],
|
||||
);
|
||||
expect(mockClient.agreeToTerms).toBeCalledWith(SERVICE_TYPES.IM, "https://imone.test", "a token token", [
|
||||
"http://example.com/one",
|
||||
"http://example.com/two",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should prompt for only services with un-agreed policies", async function() {
|
||||
it("should prompt for only services with un-agreed policies", async function () {
|
||||
const directEvent = new MatrixEvent({
|
||||
type: EventType.Direct,
|
||||
content: {
|
||||
@@ -155,16 +152,16 @@ describe('Terms', function() {
|
||||
|
||||
mockClient.getTerms.mockImplementation(async (_serviceTypes: SERVICE_TYPES, baseUrl: string) => {
|
||||
switch (baseUrl) {
|
||||
case 'https://imone.test':
|
||||
case "https://imone.test":
|
||||
return {
|
||||
policies: {
|
||||
"policy_the_first": POLICY_ONE,
|
||||
policy_the_first: POLICY_ONE,
|
||||
},
|
||||
};
|
||||
case 'https://imtwo.test':
|
||||
case "https://imtwo.test":
|
||||
return {
|
||||
policies: {
|
||||
"policy_the_second": POLICY_TWO,
|
||||
policy_the_second: POLICY_TWO,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -173,26 +170,22 @@ describe('Terms', function() {
|
||||
const interactionCallback = jest.fn().mockResolvedValue(["http://example.com/one", "http://example.com/two"]);
|
||||
await startTermsFlow([IM_SERVICE_ONE, IM_SERVICE_TWO], interactionCallback);
|
||||
|
||||
expect(interactionCallback).toBeCalledWith([
|
||||
{
|
||||
service: IM_SERVICE_TWO,
|
||||
policies: {
|
||||
policy_the_second: POLICY_TWO,
|
||||
expect(interactionCallback).toBeCalledWith(
|
||||
[
|
||||
{
|
||||
service: IM_SERVICE_TWO,
|
||||
policies: {
|
||||
policy_the_second: POLICY_TWO,
|
||||
},
|
||||
},
|
||||
},
|
||||
], ["http://example.com/one"]);
|
||||
expect(mockClient.agreeToTerms).toBeCalledWith(
|
||||
SERVICE_TYPES.IM,
|
||||
'https://imone.test',
|
||||
'a token token',
|
||||
],
|
||||
["http://example.com/one"],
|
||||
);
|
||||
expect(mockClient.agreeToTerms).toBeCalledWith(
|
||||
SERVICE_TYPES.IM,
|
||||
'https://imtwo.test',
|
||||
'a token token',
|
||||
["http://example.com/two"],
|
||||
);
|
||||
expect(mockClient.agreeToTerms).toBeCalledWith(SERVICE_TYPES.IM, "https://imone.test", "a token token", [
|
||||
"http://example.com/one",
|
||||
]);
|
||||
expect(mockClient.agreeToTerms).toBeCalledWith(SERVICE_TYPES.IM, "https://imtwo.test", "a token token", [
|
||||
"http://example.com/two",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -15,26 +15,23 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventType, MatrixClient, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
import TestRenderer from "react-test-renderer";
|
||||
import { ReactElement } from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { getSenderName, textForEvent } from "../src/TextForEvent";
|
||||
import SettingsStore from "../src/settings/SettingsStore";
|
||||
import { createTestClient, stubClient } from './test-utils';
|
||||
import { MatrixClientPeg } from '../src/MatrixClientPeg';
|
||||
import UserIdentifierCustomisations from '../src/customisations/UserIdentifier';
|
||||
import { createTestClient, stubClient } from "./test-utils";
|
||||
import { MatrixClientPeg } from "../src/MatrixClientPeg";
|
||||
import UserIdentifierCustomisations from "../src/customisations/UserIdentifier";
|
||||
import { ElementCall } from "../src/models/Call";
|
||||
|
||||
jest.mock("../src/settings/SettingsStore");
|
||||
jest.mock('../src/customisations/UserIdentifier', () => ({
|
||||
getDisplayUserIdentifier: jest.fn().mockImplementation(userId => userId),
|
||||
jest.mock("../src/customisations/UserIdentifier", () => ({
|
||||
getDisplayUserIdentifier: jest.fn().mockImplementation((userId) => userId),
|
||||
}));
|
||||
|
||||
function mockPinnedEvent(
|
||||
pinnedMessageIds?: string[],
|
||||
prevPinnedMessageIds?: string[],
|
||||
): MatrixEvent {
|
||||
function mockPinnedEvent(pinnedMessageIds?: string[], prevPinnedMessageIds?: string[]): MatrixEvent {
|
||||
return new MatrixEvent({
|
||||
type: "m.room.pinned_events",
|
||||
state_key: "",
|
||||
@@ -53,31 +50,33 @@ function mockPinnedEvent(
|
||||
// and should be replaced with snapshots.
|
||||
function renderComponent(component): string {
|
||||
const serializeObject = (object): string => {
|
||||
if (typeof object === 'string') {
|
||||
return object === ' ' ? '' : object;
|
||||
if (typeof object === "string") {
|
||||
return object === " " ? "" : object;
|
||||
}
|
||||
|
||||
if (Array.isArray(object) && object.length === 1 && typeof object[0] === 'string') {
|
||||
if (Array.isArray(object) && object.length === 1 && typeof object[0] === "string") {
|
||||
return object[0];
|
||||
}
|
||||
|
||||
if (object['type'] !== undefined && typeof object['children'] !== undefined) {
|
||||
if (object["type"] !== undefined && typeof object["children"] !== undefined) {
|
||||
return serializeObject(object.children);
|
||||
}
|
||||
|
||||
if (!Array.isArray(object)) {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
|
||||
return object.map(child => {
|
||||
return serializeObject(child);
|
||||
}).join('');
|
||||
return object
|
||||
.map((child) => {
|
||||
return serializeObject(child);
|
||||
})
|
||||
.join("");
|
||||
};
|
||||
|
||||
return serializeObject(component.toJSON());
|
||||
}
|
||||
|
||||
describe('TextForEvent', () => {
|
||||
describe("TextForEvent", () => {
|
||||
describe("getSenderName()", () => {
|
||||
it("Prefers sender.name", () => {
|
||||
expect(getSenderName({ sender: { name: "Alice" } } as MatrixEvent)).toBe("Alice");
|
||||
@@ -93,11 +92,11 @@ describe('TextForEvent', () => {
|
||||
describe("TextForPinnedEvent", () => {
|
||||
beforeAll(() => {
|
||||
// enable feature_pinning setting
|
||||
(SettingsStore.getValue as jest.Mock).mockImplementation(feature => feature === 'feature_pinning');
|
||||
(SettingsStore.getValue as jest.Mock).mockImplementation((feature) => feature === "feature_pinning");
|
||||
});
|
||||
|
||||
it("mentions message when a single message was pinned, with no previously pinned messages", () => {
|
||||
const event = mockPinnedEvent(['message-1']);
|
||||
const event = mockPinnedEvent(["message-1"]);
|
||||
const plainText = textForEvent(event);
|
||||
const component = TestRenderer.create(textForEvent(event, true) as ReactElement);
|
||||
|
||||
@@ -107,7 +106,7 @@ describe('TextForEvent', () => {
|
||||
});
|
||||
|
||||
it("mentions message when a single message was pinned, with multiple previously pinned messages", () => {
|
||||
const event = mockPinnedEvent(['message-1', 'message-2', 'message-3'], ['message-1', 'message-2']);
|
||||
const event = mockPinnedEvent(["message-1", "message-2", "message-3"], ["message-1", "message-2"]);
|
||||
const plainText = textForEvent(event);
|
||||
const component = TestRenderer.create(textForEvent(event, true) as ReactElement);
|
||||
|
||||
@@ -117,7 +116,7 @@ describe('TextForEvent', () => {
|
||||
});
|
||||
|
||||
it("mentions message when a single message was unpinned, with a single message previously pinned", () => {
|
||||
const event = mockPinnedEvent([], ['message-1']);
|
||||
const event = mockPinnedEvent([], ["message-1"]);
|
||||
const plainText = textForEvent(event);
|
||||
const component = TestRenderer.create(textForEvent(event, true) as ReactElement);
|
||||
|
||||
@@ -127,7 +126,7 @@ describe('TextForEvent', () => {
|
||||
});
|
||||
|
||||
it("mentions message when a single message was unpinned, with multiple previously pinned messages", () => {
|
||||
const event = mockPinnedEvent(['message-2'], ['message-1', 'message-2']);
|
||||
const event = mockPinnedEvent(["message-2"], ["message-1", "message-2"]);
|
||||
const plainText = textForEvent(event);
|
||||
const component = TestRenderer.create(textForEvent(event, true) as ReactElement);
|
||||
|
||||
@@ -137,7 +136,7 @@ describe('TextForEvent', () => {
|
||||
});
|
||||
|
||||
it("shows generic text when multiple messages were pinned", () => {
|
||||
const event = mockPinnedEvent(['message-1', 'message-2', 'message-3'], ['message-1']);
|
||||
const event = mockPinnedEvent(["message-1", "message-2", "message-3"], ["message-1"]);
|
||||
const plainText = textForEvent(event);
|
||||
const component = TestRenderer.create(textForEvent(event, true) as ReactElement);
|
||||
|
||||
@@ -147,7 +146,7 @@ describe('TextForEvent', () => {
|
||||
});
|
||||
|
||||
it("shows generic text when multiple messages were unpinned", () => {
|
||||
const event = mockPinnedEvent(['message-3'], ['message-1', 'message-2', 'message-3']);
|
||||
const event = mockPinnedEvent(["message-3"], ["message-1", "message-2", "message-3"]);
|
||||
const plainText = textForEvent(event);
|
||||
const component = TestRenderer.create(textForEvent(event, true) as ReactElement);
|
||||
|
||||
@@ -157,7 +156,7 @@ describe('TextForEvent', () => {
|
||||
});
|
||||
|
||||
it("shows generic text when one message was pinned, and another unpinned", () => {
|
||||
const event = mockPinnedEvent(['message-2'], ['message-1']);
|
||||
const event = mockPinnedEvent(["message-2"], ["message-1"]);
|
||||
const plainText = textForEvent(event);
|
||||
const component = TestRenderer.create(textForEvent(event, true) as ReactElement);
|
||||
|
||||
@@ -174,19 +173,19 @@ describe('TextForEvent', () => {
|
||||
};
|
||||
|
||||
const userA = {
|
||||
id: '@a',
|
||||
name: 'Alice',
|
||||
rawDisplayName: 'Alice',
|
||||
id: "@a",
|
||||
name: "Alice",
|
||||
rawDisplayName: "Alice",
|
||||
};
|
||||
const userB = {
|
||||
id: '@b',
|
||||
name: 'Bob (@b)',
|
||||
rawDisplayName: 'Bob',
|
||||
id: "@b",
|
||||
name: "Bob (@b)",
|
||||
rawDisplayName: "Bob",
|
||||
};
|
||||
const userC = {
|
||||
id: '@c',
|
||||
name: 'Bob (@c)',
|
||||
rawDisplayName: 'Bob',
|
||||
id: "@c",
|
||||
name: "Bob (@c)",
|
||||
rawDisplayName: "Bob",
|
||||
};
|
||||
interface PowerEventProps {
|
||||
usersDefault?: number;
|
||||
@@ -194,9 +193,7 @@ describe('TextForEvent', () => {
|
||||
users: Record<string, number>;
|
||||
prevUsers: Record<string, number>;
|
||||
}
|
||||
const mockPowerEvent = ({
|
||||
usersDefault, prevDefault, users, prevUsers,
|
||||
}: PowerEventProps): MatrixEvent => {
|
||||
const mockPowerEvent = ({ usersDefault, prevDefault, users, prevUsers }: PowerEventProps): MatrixEvent => {
|
||||
const mxEvent = new MatrixEvent({
|
||||
type: EventType.RoomPowerLevels,
|
||||
sender: userA.id,
|
||||
@@ -218,16 +215,16 @@ describe('TextForEvent', () => {
|
||||
mockClient = createTestClient();
|
||||
MatrixClientPeg.get = () => mockClient;
|
||||
mockClient.getRoom.mockClear().mockReturnValue(mockRoom);
|
||||
mockRoom.getMember.mockClear().mockImplementation(
|
||||
userId => [userA, userB, userC].find(u => u.id === userId),
|
||||
);
|
||||
mockRoom.getMember
|
||||
.mockClear()
|
||||
.mockImplementation((userId) => [userA, userB, userC].find((u) => u.id === userId));
|
||||
(SettingsStore.getValue as jest.Mock).mockReturnValue(true);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
(UserIdentifierCustomisations.getDisplayUserIdentifier as jest.Mock)
|
||||
.mockClear()
|
||||
.mockImplementation(userId => userId);
|
||||
.mockImplementation((userId) => userId);
|
||||
});
|
||||
|
||||
it("returns falsy when no users have changed power level", () => {
|
||||
@@ -308,80 +305,112 @@ describe('TextForEvent', () => {
|
||||
[userC.id]: 101,
|
||||
},
|
||||
});
|
||||
const expectedText = "Alice changed the power level of Bob (@b) from Moderator to Admin,"
|
||||
+ " Bob (@c) from Custom (101) to Moderator.";
|
||||
const expectedText =
|
||||
"Alice changed the power level of Bob (@b) from Moderator to Admin," +
|
||||
" Bob (@c) from Custom (101) to Moderator.";
|
||||
expect(textForEvent(event)).toEqual(expectedText);
|
||||
});
|
||||
});
|
||||
|
||||
describe("textForCanonicalAliasEvent()", () => {
|
||||
const userA = {
|
||||
id: '@a',
|
||||
name: 'Alice',
|
||||
id: "@a",
|
||||
name: "Alice",
|
||||
};
|
||||
|
||||
interface AliasEventProps {
|
||||
alias?: string; prevAlias?: string; altAliases?: string[]; prevAltAliases?: string[];
|
||||
alias?: string;
|
||||
prevAlias?: string;
|
||||
altAliases?: string[];
|
||||
prevAltAliases?: string[];
|
||||
}
|
||||
const mockEvent = ({
|
||||
alias, prevAlias, altAliases, prevAltAliases,
|
||||
}: AliasEventProps): MatrixEvent => new MatrixEvent({
|
||||
type: EventType.RoomCanonicalAlias,
|
||||
sender: userA.id,
|
||||
state_key: "",
|
||||
content: {
|
||||
alias, alt_aliases: altAliases,
|
||||
},
|
||||
prev_content: {
|
||||
alias: prevAlias, alt_aliases: prevAltAliases,
|
||||
},
|
||||
});
|
||||
const mockEvent = ({ alias, prevAlias, altAliases, prevAltAliases }: AliasEventProps): MatrixEvent =>
|
||||
new MatrixEvent({
|
||||
type: EventType.RoomCanonicalAlias,
|
||||
sender: userA.id,
|
||||
state_key: "",
|
||||
content: {
|
||||
alias,
|
||||
alt_aliases: altAliases,
|
||||
},
|
||||
prev_content: {
|
||||
alias: prevAlias,
|
||||
alt_aliases: prevAltAliases,
|
||||
},
|
||||
});
|
||||
|
||||
type TestCase = [string, AliasEventProps & { result: string }];
|
||||
const testCases: TestCase[] = [
|
||||
["room alias didn't change", {
|
||||
result: '@a changed the addresses for this room.',
|
||||
}],
|
||||
["room alias changed", {
|
||||
alias: 'banana',
|
||||
prevAlias: 'apple',
|
||||
result: '@a set the main address for this room to banana.',
|
||||
}],
|
||||
["room alias was added", {
|
||||
alias: 'banana',
|
||||
result: '@a set the main address for this room to banana.',
|
||||
}],
|
||||
["room alias was removed", {
|
||||
prevAlias: 'apple',
|
||||
result: '@a removed the main address for this room.',
|
||||
}],
|
||||
["added an alt alias", {
|
||||
altAliases: ['canteloupe'],
|
||||
result: '@a added alternative address canteloupe for this room.',
|
||||
}],
|
||||
["added multiple alt aliases", {
|
||||
altAliases: ['canteloupe', 'date'],
|
||||
result: '@a added the alternative addresses canteloupe, date for this room.',
|
||||
}],
|
||||
["removed an alt alias", {
|
||||
altAliases: ['canteloupe'],
|
||||
prevAltAliases: ['canteloupe', 'date'],
|
||||
result: '@a removed alternative address date for this room.',
|
||||
}],
|
||||
["added and removed an alt aliases", {
|
||||
altAliases: ['canteloupe', 'elderberry'],
|
||||
prevAltAliases: ['canteloupe', 'date'],
|
||||
result: '@a changed the alternative addresses for this room.',
|
||||
}],
|
||||
["changed alias and added alt alias", {
|
||||
alias: 'banana',
|
||||
prevAlias: 'apple',
|
||||
altAliases: ['canteloupe'],
|
||||
result: '@a changed the main and alternative addresses for this room.',
|
||||
}],
|
||||
[
|
||||
"room alias didn't change",
|
||||
{
|
||||
result: "@a changed the addresses for this room.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"room alias changed",
|
||||
{
|
||||
alias: "banana",
|
||||
prevAlias: "apple",
|
||||
result: "@a set the main address for this room to banana.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"room alias was added",
|
||||
{
|
||||
alias: "banana",
|
||||
result: "@a set the main address for this room to banana.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"room alias was removed",
|
||||
{
|
||||
prevAlias: "apple",
|
||||
result: "@a removed the main address for this room.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"added an alt alias",
|
||||
{
|
||||
altAliases: ["canteloupe"],
|
||||
result: "@a added alternative address canteloupe for this room.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"added multiple alt aliases",
|
||||
{
|
||||
altAliases: ["canteloupe", "date"],
|
||||
result: "@a added the alternative addresses canteloupe, date for this room.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"removed an alt alias",
|
||||
{
|
||||
altAliases: ["canteloupe"],
|
||||
prevAltAliases: ["canteloupe", "date"],
|
||||
result: "@a removed alternative address date for this room.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"added and removed an alt aliases",
|
||||
{
|
||||
altAliases: ["canteloupe", "elderberry"],
|
||||
prevAltAliases: ["canteloupe", "date"],
|
||||
result: "@a changed the alternative addresses for this room.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"changed alias and added alt alias",
|
||||
{
|
||||
alias: "banana",
|
||||
prevAlias: "apple",
|
||||
altAliases: ["canteloupe"],
|
||||
result: "@a changed the main and alternative addresses for this room.",
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
it.each(testCases)('returns correct message when %s', (_d, { result, ...eventProps }) => {
|
||||
it.each(testCases)("returns correct message when %s", (_d, { result, ...eventProps }) => {
|
||||
const event = mockEvent(eventProps);
|
||||
expect(textForEvent(event)).toEqual(result);
|
||||
});
|
||||
@@ -392,18 +421,15 @@ describe('TextForEvent', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
pollEvent = new MatrixEvent({
|
||||
type: 'org.matrix.msc3381.poll.start',
|
||||
sender: '@a',
|
||||
type: "org.matrix.msc3381.poll.start",
|
||||
sender: "@a",
|
||||
content: {
|
||||
'org.matrix.msc3381.poll.start': {
|
||||
answers: [
|
||||
{ 'org.matrix.msc1767.text': 'option1' },
|
||||
{ 'org.matrix.msc1767.text': 'option2' },
|
||||
],
|
||||
"org.matrix.msc3381.poll.start": {
|
||||
answers: [{ "org.matrix.msc1767.text": "option1" }, { "org.matrix.msc1767.text": "option2" }],
|
||||
question: {
|
||||
'body': 'Test poll name',
|
||||
'msgtype': 'm.text',
|
||||
'org.matrix.msc1767.text': 'Test poll name',
|
||||
"body": "Test poll name",
|
||||
"msgtype": "m.text",
|
||||
"org.matrix.msc1767.text": "Test poll name",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -413,11 +439,11 @@ describe('TextForEvent', () => {
|
||||
it("returns correct message for redacted poll start", () => {
|
||||
pollEvent.makeRedacted(pollEvent);
|
||||
|
||||
expect(textForEvent(pollEvent)).toEqual('@a: Message deleted');
|
||||
expect(textForEvent(pollEvent)).toEqual("@a: Message deleted");
|
||||
});
|
||||
|
||||
it("returns correct message for normal poll start", () => {
|
||||
expect(textForEvent(pollEvent)).toEqual('@a has started a poll - ');
|
||||
expect(textForEvent(pollEvent)).toEqual("@a has started a poll - ");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -426,12 +452,12 @@ describe('TextForEvent', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
messageEvent = new MatrixEvent({
|
||||
type: 'm.room.message',
|
||||
sender: '@a',
|
||||
type: "m.room.message",
|
||||
sender: "@a",
|
||||
content: {
|
||||
'body': 'test message',
|
||||
'msgtype': 'm.text',
|
||||
'org.matrix.msc1767.text': 'test message',
|
||||
"body": "test message",
|
||||
"msgtype": "m.text",
|
||||
"org.matrix.msc1767.text": "test message",
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -439,11 +465,11 @@ describe('TextForEvent', () => {
|
||||
it("returns correct message for redacted message", () => {
|
||||
messageEvent.makeRedacted(messageEvent);
|
||||
|
||||
expect(textForEvent(messageEvent)).toEqual('@a: Message deleted');
|
||||
expect(textForEvent(messageEvent)).toEqual("@a: Message deleted");
|
||||
});
|
||||
|
||||
it("returns correct message for normal message", () => {
|
||||
expect(textForEvent(messageEvent)).toEqual('@a: test message');
|
||||
expect(textForEvent(messageEvent)).toEqual("@a: test message");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -472,14 +498,14 @@ describe('TextForEvent', () => {
|
||||
});
|
||||
|
||||
it("returns correct message for call event when supported", () => {
|
||||
expect(textForEvent(callEvent)).toEqual('Video call started in Test room.');
|
||||
expect(textForEvent(callEvent)).toEqual("Video call started in Test room.");
|
||||
});
|
||||
|
||||
it("returns correct message for call event when supported", () => {
|
||||
mocked(mockClient).supportsVoip.mockReturnValue(false);
|
||||
|
||||
expect(textForEvent(callEvent)).toEqual(
|
||||
'Video call started in Test room. (not supported by this browser)',
|
||||
"Video call started in Test room. (not supported by this browser)",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,11 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import { mocked } from "jest-mock";
|
||||
import {
|
||||
MatrixEvent,
|
||||
EventType,
|
||||
MsgType,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixEvent, EventType, MsgType } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { haveRendererForEvent } from "../src/events/EventTileFactory";
|
||||
import { getMockClientWithEventEmitter, makeBeaconEvent, mockClientMethodsUser } from "./test-utils";
|
||||
@@ -29,9 +25,9 @@ jest.mock("../src/events/EventTileFactory", () => ({
|
||||
haveRendererForEvent: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('eventTriggersUnreadCount()', () => {
|
||||
const aliceId = '@alice:server.org';
|
||||
const bobId = '@bob:server.org';
|
||||
describe("eventTriggersUnreadCount()", () => {
|
||||
const aliceId = "@alice:server.org";
|
||||
const bobId = "@bob:server.org";
|
||||
|
||||
// mock user credentials
|
||||
getMockClientWithEventEmitter({
|
||||
@@ -44,7 +40,7 @@ describe('eventTriggersUnreadCount()', () => {
|
||||
sender: aliceId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello from Alice',
|
||||
body: "Hello from Alice",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -53,7 +49,7 @@ describe('eventTriggersUnreadCount()', () => {
|
||||
sender: bobId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello from Bob',
|
||||
body: "Hello from Bob",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -68,31 +64,31 @@ describe('eventTriggersUnreadCount()', () => {
|
||||
mocked(haveRendererForEvent).mockClear().mockReturnValue(false);
|
||||
});
|
||||
|
||||
it('returns false when the event was sent by the current user', () => {
|
||||
it("returns false when the event was sent by the current user", () => {
|
||||
expect(eventTriggersUnreadCount(bobsMessage)).toBe(false);
|
||||
// returned early before checking renderer
|
||||
expect(haveRendererForEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns false for a redacted event', () => {
|
||||
it("returns false for a redacted event", () => {
|
||||
expect(eventTriggersUnreadCount(redactedEvent)).toBe(false);
|
||||
// returned early before checking renderer
|
||||
expect(haveRendererForEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns false for an event without a renderer', () => {
|
||||
it("returns false for an event without a renderer", () => {
|
||||
mocked(haveRendererForEvent).mockReturnValue(false);
|
||||
expect(eventTriggersUnreadCount(alicesMessage)).toBe(false);
|
||||
expect(haveRendererForEvent).toHaveBeenCalledWith(alicesMessage, false);
|
||||
});
|
||||
|
||||
it('returns true for an event with a renderer', () => {
|
||||
it("returns true for an event with a renderer", () => {
|
||||
mocked(haveRendererForEvent).mockReturnValue(true);
|
||||
expect(eventTriggersUnreadCount(alicesMessage)).toBe(true);
|
||||
expect(haveRendererForEvent).toHaveBeenCalledWith(alicesMessage, false);
|
||||
});
|
||||
|
||||
it('returns false for beacon locations', () => {
|
||||
it("returns false for beacon locations", () => {
|
||||
const beaconLocationEvent = makeBeaconEvent(aliceId);
|
||||
expect(eventTriggersUnreadCount(beaconLocationEvent)).toBe(false);
|
||||
expect(haveRendererForEvent).not.toHaveBeenCalled();
|
||||
@@ -107,7 +103,7 @@ describe('eventTriggersUnreadCount()', () => {
|
||||
EventType.RoomServerAcl,
|
||||
];
|
||||
|
||||
it.each(noUnreadEventTypes)('returns false without checking for renderer for events with type %s', (eventType) => {
|
||||
it.each(noUnreadEventTypes)("returns false without checking for renderer for events with type %s", (eventType) => {
|
||||
const event = new MatrixEvent({
|
||||
type: eventType,
|
||||
sender: aliceId,
|
||||
|
||||
@@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import FakeTimers from '@sinonjs/fake-timers';
|
||||
import EventEmitter from 'events';
|
||||
import FakeTimers from "@sinonjs/fake-timers";
|
||||
import EventEmitter from "events";
|
||||
|
||||
import UserActivity from '../src/UserActivity';
|
||||
import UserActivity from "../src/UserActivity";
|
||||
|
||||
class FakeDomEventEmitter extends EventEmitter {
|
||||
addEventListener(what, l) {
|
||||
@@ -29,13 +29,13 @@ class FakeDomEventEmitter extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
describe('UserActivity', function() {
|
||||
describe("UserActivity", function () {
|
||||
let fakeWindow;
|
||||
let fakeDocument;
|
||||
let userActivity;
|
||||
let clock;
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
fakeWindow = new FakeDomEventEmitter();
|
||||
fakeDocument = new FakeDomEventEmitter();
|
||||
userActivity = new UserActivity(fakeWindow, fakeDocument);
|
||||
@@ -43,26 +43,26 @@ describe('UserActivity', function() {
|
||||
clock = FakeTimers.install();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
afterEach(function () {
|
||||
userActivity.stop();
|
||||
userActivity = null;
|
||||
clock.uninstall();
|
||||
clock = null;
|
||||
});
|
||||
|
||||
it('should return the same shared instance', function() {
|
||||
it("should return the same shared instance", function () {
|
||||
expect(UserActivity.sharedInstance()).toBe(UserActivity.sharedInstance());
|
||||
});
|
||||
|
||||
it('should consider user inactive if no activity', function() {
|
||||
it("should consider user inactive if no activity", function () {
|
||||
expect(userActivity.userActiveNow()).toBe(false);
|
||||
});
|
||||
|
||||
it('should consider user not active recently if no activity', function() {
|
||||
it("should consider user not active recently if no activity", function () {
|
||||
expect(userActivity.userActiveRecently()).toBe(false);
|
||||
});
|
||||
|
||||
it('should not consider user active after activity if no window focus', function() {
|
||||
it("should not consider user active after activity if no window focus", function () {
|
||||
fakeDocument.hasFocus = jest.fn().mockReturnValue(false);
|
||||
|
||||
userActivity.onUserActivity({});
|
||||
@@ -70,7 +70,7 @@ describe('UserActivity', function() {
|
||||
expect(userActivity.userActiveRecently()).toBe(false);
|
||||
});
|
||||
|
||||
it('should consider user active shortly after activity', function() {
|
||||
it("should consider user active shortly after activity", function () {
|
||||
fakeDocument.hasFocus = jest.fn().mockReturnValue(true);
|
||||
|
||||
userActivity.onUserActivity({});
|
||||
@@ -81,7 +81,7 @@ describe('UserActivity', function() {
|
||||
expect(userActivity.userActiveRecently()).toBe(true);
|
||||
});
|
||||
|
||||
it('should consider user not active after 10s of no activity', function() {
|
||||
it("should consider user not active after 10s of no activity", function () {
|
||||
fakeDocument.hasFocus = jest.fn().mockReturnValue(true);
|
||||
|
||||
userActivity.onUserActivity({});
|
||||
@@ -89,7 +89,7 @@ describe('UserActivity', function() {
|
||||
expect(userActivity.userActiveNow()).toBe(false);
|
||||
});
|
||||
|
||||
it('should consider user passive after 10s of no activity', function() {
|
||||
it("should consider user passive after 10s of no activity", function () {
|
||||
fakeDocument.hasFocus = jest.fn().mockReturnValue(true);
|
||||
|
||||
userActivity.onUserActivity({});
|
||||
@@ -97,19 +97,19 @@ describe('UserActivity', function() {
|
||||
expect(userActivity.userActiveRecently()).toBe(true);
|
||||
});
|
||||
|
||||
it('should not consider user passive after 10s if window un-focused', function() {
|
||||
it("should not consider user passive after 10s if window un-focused", function () {
|
||||
fakeDocument.hasFocus = jest.fn().mockReturnValue(true);
|
||||
|
||||
userActivity.onUserActivity({});
|
||||
clock.tick(10000);
|
||||
|
||||
fakeDocument.hasFocus = jest.fn().mockReturnValue(false);
|
||||
fakeWindow.emit('blur', {});
|
||||
fakeWindow.emit("blur", {});
|
||||
|
||||
expect(userActivity.userActiveRecently()).toBe(false);
|
||||
});
|
||||
|
||||
it('should not consider user passive after 3 mins', function() {
|
||||
it("should not consider user passive after 3 mins", function () {
|
||||
fakeDocument.hasFocus = jest.fn().mockReturnValue(true);
|
||||
|
||||
userActivity.onUserActivity({});
|
||||
@@ -118,7 +118,7 @@ describe('UserActivity', function() {
|
||||
expect(userActivity.userActiveRecently()).toBe(false);
|
||||
});
|
||||
|
||||
it('should extend timer on activity', function() {
|
||||
it("should extend timer on activity", function () {
|
||||
fakeDocument.hasFocus = jest.fn().mockReturnValue(true);
|
||||
|
||||
userActivity.onUserActivity({});
|
||||
|
||||
@@ -41,8 +41,8 @@ describe("KeyboardShortcutUtils", () => {
|
||||
it("doesn't change KEYBOARD_SHORTCUTS when getting shortcuts", async () => {
|
||||
mockKeyboardShortcuts({
|
||||
KEYBOARD_SHORTCUTS: {
|
||||
"Keybind1": {},
|
||||
"Keybind2": {},
|
||||
Keybind1: {},
|
||||
Keybind2: {},
|
||||
},
|
||||
MAC_ONLY_SHORTCUTS: ["Keybind1"],
|
||||
DESKTOP_SHORTCUTS: ["Keybind2"],
|
||||
@@ -62,29 +62,29 @@ describe("KeyboardShortcutUtils", () => {
|
||||
it("when on web and not on macOS ", async () => {
|
||||
mockKeyboardShortcuts({
|
||||
KEYBOARD_SHORTCUTS: {
|
||||
"Keybind1": {},
|
||||
"Keybind2": {},
|
||||
"Keybind3": { "controller": { settingDisabled: true } },
|
||||
"Keybind4": {},
|
||||
Keybind1: {},
|
||||
Keybind2: {},
|
||||
Keybind3: { controller: { settingDisabled: true } },
|
||||
Keybind4: {},
|
||||
},
|
||||
MAC_ONLY_SHORTCUTS: ["Keybind1"],
|
||||
DESKTOP_SHORTCUTS: ["Keybind2"],
|
||||
});
|
||||
mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) });
|
||||
expect((await getUtils()).getKeyboardShortcuts()).toEqual({ "Keybind4": {} });
|
||||
expect((await getUtils()).getKeyboardShortcuts()).toEqual({ Keybind4: {} });
|
||||
});
|
||||
|
||||
it("when on desktop", async () => {
|
||||
mockKeyboardShortcuts({
|
||||
KEYBOARD_SHORTCUTS: {
|
||||
"Keybind1": {},
|
||||
"Keybind2": {},
|
||||
Keybind1: {},
|
||||
Keybind2: {},
|
||||
},
|
||||
MAC_ONLY_SHORTCUTS: [],
|
||||
DESKTOP_SHORTCUTS: ["Keybind2"],
|
||||
});
|
||||
mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(true) });
|
||||
expect((await getUtils()).getKeyboardShortcuts()).toEqual({ "Keybind1": {}, "Keybind2": {} });
|
||||
expect((await getUtils()).getKeyboardShortcuts()).toEqual({ Keybind1: {}, Keybind2: {} });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,26 +46,38 @@ const button4 = <Button key={4}>d</Button>;
|
||||
|
||||
// mock offsetParent
|
||||
Object.defineProperty(HTMLElement.prototype, "offsetParent", {
|
||||
get() { return this.parentNode; },
|
||||
get() {
|
||||
return this.parentNode;
|
||||
},
|
||||
});
|
||||
|
||||
describe("RovingTabIndex", () => {
|
||||
it("RovingTabIndexProvider renders children as expected", () => {
|
||||
const { container } = render(<RovingTabIndexProvider>
|
||||
{ () => <div><span>Test</span></div> }
|
||||
</RovingTabIndexProvider>);
|
||||
const { container } = render(
|
||||
<RovingTabIndexProvider>
|
||||
{() => (
|
||||
<div>
|
||||
<span>Test</span>
|
||||
</div>
|
||||
)}
|
||||
</RovingTabIndexProvider>,
|
||||
);
|
||||
expect(container.textContent).toBe("Test");
|
||||
expect(container.innerHTML).toBe('<div><span>Test</span></div>');
|
||||
expect(container.innerHTML).toBe("<div><span>Test</span></div>");
|
||||
});
|
||||
|
||||
it("RovingTabIndexProvider works as expected with useRovingTabIndex", () => {
|
||||
const { container, rerender } = render(<RovingTabIndexProvider>
|
||||
{ () => <React.Fragment>
|
||||
{ button1 }
|
||||
{ button2 }
|
||||
{ button3 }
|
||||
</React.Fragment> }
|
||||
</RovingTabIndexProvider>);
|
||||
const { container, rerender } = render(
|
||||
<RovingTabIndexProvider>
|
||||
{() => (
|
||||
<React.Fragment>
|
||||
{button1}
|
||||
{button2}
|
||||
{button3}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</RovingTabIndexProvider>,
|
||||
);
|
||||
|
||||
// should begin with 0th being active
|
||||
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
||||
@@ -83,42 +95,57 @@ describe("RovingTabIndex", () => {
|
||||
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
|
||||
|
||||
// update the children, it should remain on the same button
|
||||
rerender(<RovingTabIndexProvider>
|
||||
{ () => <React.Fragment>
|
||||
{ button1 }
|
||||
{ button4 }
|
||||
{ button2 }
|
||||
{ button3 }
|
||||
</React.Fragment> }
|
||||
</RovingTabIndexProvider>);
|
||||
rerender(
|
||||
<RovingTabIndexProvider>
|
||||
{() => (
|
||||
<React.Fragment>
|
||||
{button1}
|
||||
{button4}
|
||||
{button2}
|
||||
{button3}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</RovingTabIndexProvider>,
|
||||
);
|
||||
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0, -1]);
|
||||
|
||||
// update the children, remove the active button, it should move to the next one
|
||||
rerender(<RovingTabIndexProvider>
|
||||
{ () => <React.Fragment>
|
||||
{ button1 }
|
||||
{ button4 }
|
||||
{ button3 }
|
||||
</React.Fragment> }
|
||||
</RovingTabIndexProvider>);
|
||||
rerender(
|
||||
<RovingTabIndexProvider>
|
||||
{() => (
|
||||
<React.Fragment>
|
||||
{button1}
|
||||
{button4}
|
||||
{button3}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</RovingTabIndexProvider>,
|
||||
);
|
||||
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
|
||||
});
|
||||
|
||||
it("RovingTabIndexProvider works as expected with RovingTabIndexWrapper", () => {
|
||||
const { container } = render(<RovingTabIndexProvider>
|
||||
{ () => <React.Fragment>
|
||||
{ button1 }
|
||||
{ button2 }
|
||||
<RovingTabIndexWrapper>
|
||||
{ ({ onFocus, isActive, ref }) =>
|
||||
<button
|
||||
onFocus={onFocus}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
ref={ref as React.RefObject<HTMLButtonElement>}>.</button>
|
||||
}
|
||||
</RovingTabIndexWrapper>
|
||||
</React.Fragment> }
|
||||
</RovingTabIndexProvider>);
|
||||
const { container } = render(
|
||||
<RovingTabIndexProvider>
|
||||
{() => (
|
||||
<React.Fragment>
|
||||
{button1}
|
||||
{button2}
|
||||
<RovingTabIndexWrapper>
|
||||
{({ onFocus, isActive, ref }) => (
|
||||
<button
|
||||
onFocus={onFocus}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
ref={ref as React.RefObject<HTMLButtonElement>}
|
||||
>
|
||||
.
|
||||
</button>
|
||||
)}
|
||||
</RovingTabIndexWrapper>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</RovingTabIndexProvider>,
|
||||
);
|
||||
|
||||
// should begin with 0th being active
|
||||
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
||||
@@ -132,15 +159,20 @@ describe("RovingTabIndex", () => {
|
||||
it("SetFocus works as expected", () => {
|
||||
const ref1 = React.createRef<HTMLElement>();
|
||||
const ref2 = React.createRef<HTMLElement>();
|
||||
expect(reducer({
|
||||
activeRef: ref1,
|
||||
refs: [ref1, ref2],
|
||||
}, {
|
||||
type: Type.SetFocus,
|
||||
payload: {
|
||||
ref: ref2,
|
||||
},
|
||||
})).toStrictEqual({
|
||||
expect(
|
||||
reducer(
|
||||
{
|
||||
activeRef: ref1,
|
||||
refs: [ref1, ref2],
|
||||
},
|
||||
{
|
||||
type: Type.SetFocus,
|
||||
payload: {
|
||||
ref: ref2,
|
||||
},
|
||||
},
|
||||
),
|
||||
).toStrictEqual({
|
||||
activeRef: ref2,
|
||||
refs: [ref1, ref2],
|
||||
});
|
||||
@@ -208,12 +240,14 @@ describe("RovingTabIndex", () => {
|
||||
const ref3 = React.createRef<HTMLElement>();
|
||||
const ref4 = React.createRef<HTMLElement>();
|
||||
|
||||
render(<React.Fragment>
|
||||
<span ref={ref1} />
|
||||
<span ref={ref2} />
|
||||
<span ref={ref3} />
|
||||
<span ref={ref4} />
|
||||
</React.Fragment>);
|
||||
render(
|
||||
<React.Fragment>
|
||||
<span ref={ref1} />
|
||||
<span ref={ref2} />
|
||||
<span ref={ref3} />
|
||||
<span ref={ref4} />
|
||||
</React.Fragment>,
|
||||
);
|
||||
|
||||
let state: IState = {
|
||||
activeRef: null,
|
||||
@@ -337,4 +371,3 @@ describe("RovingTabIndex", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -19,14 +19,14 @@ import { UserTab } from "../../../src/components/views/dialogs/UserTab";
|
||||
import { Action } from "../../../src/dispatcher/actions";
|
||||
import defaultDispatcher from "../../../src/dispatcher/dispatcher";
|
||||
|
||||
describe('viewUserDeviceSettings()', () => {
|
||||
const dispatchSpy = jest.spyOn(defaultDispatcher, 'dispatch');
|
||||
describe("viewUserDeviceSettings()", () => {
|
||||
const dispatchSpy = jest.spyOn(defaultDispatcher, "dispatch");
|
||||
|
||||
beforeEach(() => {
|
||||
dispatchSpy.mockClear();
|
||||
});
|
||||
|
||||
it('dispatches action to view new session manager when enabled', () => {
|
||||
it("dispatches action to view new session manager when enabled", () => {
|
||||
const isNewDeviceManagerEnabled = true;
|
||||
viewUserDeviceSettings(isNewDeviceManagerEnabled);
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('viewUserDeviceSettings()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('dispatches action to view old session manager when disabled', () => {
|
||||
it("dispatches action to view old session manager when disabled", () => {
|
||||
const isNewDeviceManagerEnabled = false;
|
||||
viewUserDeviceSettings(isNewDeviceManagerEnabled);
|
||||
|
||||
|
||||
@@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { mocked } from 'jest-mock';
|
||||
import { logger } from 'matrix-js-sdk/src/logger';
|
||||
import { mocked } from "jest-mock";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { createAudioContext, decodeOgg } from '../../src/audio/compat';
|
||||
import { createAudioContext, decodeOgg } from "../../src/audio/compat";
|
||||
import { Playback, PlaybackState } from "../../src/audio/Playback";
|
||||
|
||||
jest.mock('../../src/audio/compat', () => ({
|
||||
jest.mock("../../src/audio/compat", () => ({
|
||||
createAudioContext: jest.fn(),
|
||||
decodeOgg: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('Playback', () => {
|
||||
describe("Playback", () => {
|
||||
const mockAudioBufferSourceNode = {
|
||||
addEventListener: jest.fn(),
|
||||
connect: jest.fn(),
|
||||
@@ -47,18 +47,16 @@ describe('Playback', () => {
|
||||
const mockChannelData = new Float32Array();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(logger, 'error').mockRestore();
|
||||
jest.spyOn(logger, "error").mockRestore();
|
||||
mockAudioBuffer.getChannelData.mockClear().mockReturnValue(mockChannelData);
|
||||
mockAudioContext.decodeAudioData.mockReset().mockImplementation(
|
||||
(_b, callback) => callback(mockAudioBuffer),
|
||||
);
|
||||
mockAudioContext.decodeAudioData.mockReset().mockImplementation((_b, callback) => callback(mockAudioBuffer));
|
||||
mockAudioContext.resume.mockClear().mockResolvedValue(undefined);
|
||||
mockAudioContext.suspend.mockClear().mockResolvedValue(undefined);
|
||||
mocked(decodeOgg).mockClear().mockResolvedValue(new ArrayBuffer(1));
|
||||
mocked(createAudioContext).mockReturnValue(mockAudioContext as unknown as AudioContext);
|
||||
});
|
||||
|
||||
it('initialises correctly', () => {
|
||||
it("initialises correctly", () => {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
|
||||
const playback = new Playback(buffer);
|
||||
@@ -71,7 +69,7 @@ describe('Playback', () => {
|
||||
expect(playback.currentState).toEqual(PlaybackState.Decoding);
|
||||
});
|
||||
|
||||
it('toggles playback on from stopped state', async () => {
|
||||
it("toggles playback on from stopped state", async () => {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const playback = new Playback(buffer);
|
||||
await playback.prepare();
|
||||
@@ -83,7 +81,7 @@ describe('Playback', () => {
|
||||
expect(playback.currentState).toEqual(PlaybackState.Playing);
|
||||
});
|
||||
|
||||
it('toggles playback to paused from playing state', async () => {
|
||||
it("toggles playback to paused from playing state", async () => {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const playback = new Playback(buffer);
|
||||
await playback.prepare();
|
||||
@@ -96,7 +94,7 @@ describe('Playback', () => {
|
||||
expect(playback.currentState).toEqual(PlaybackState.Paused);
|
||||
});
|
||||
|
||||
it('stop playbacks', async () => {
|
||||
it("stop playbacks", async () => {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const playback = new Playback(buffer);
|
||||
await playback.prepare();
|
||||
@@ -109,8 +107,8 @@ describe('Playback', () => {
|
||||
expect(playback.currentState).toEqual(PlaybackState.Stopped);
|
||||
});
|
||||
|
||||
describe('prepare()', () => {
|
||||
it('decodes audio data when not greater than 5mb', async () => {
|
||||
describe("prepare()", () => {
|
||||
it("decodes audio data when not greater than 5mb", async () => {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
|
||||
const playback = new Playback(buffer);
|
||||
@@ -127,18 +125,16 @@ describe('Playback', () => {
|
||||
expect(playback.currentState).toEqual(PlaybackState.Stopped);
|
||||
});
|
||||
|
||||
it('tries to decode ogg when decodeAudioData fails', async () => {
|
||||
it("tries to decode ogg when decodeAudioData fails", async () => {
|
||||
// stub logger to keep console clean from expected error
|
||||
jest.spyOn(logger, 'error').mockReturnValue(undefined);
|
||||
jest.spyOn(logger, 'warn').mockReturnValue(undefined);
|
||||
jest.spyOn(logger, "error").mockReturnValue(undefined);
|
||||
jest.spyOn(logger, "warn").mockReturnValue(undefined);
|
||||
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const decodingError = new Error('test');
|
||||
mockAudioContext.decodeAudioData.mockImplementationOnce(
|
||||
(_b, _callback, error) => error(decodingError),
|
||||
).mockImplementationOnce(
|
||||
(_b, callback) => callback(mockAudioBuffer),
|
||||
);
|
||||
const decodingError = new Error("test");
|
||||
mockAudioContext.decodeAudioData
|
||||
.mockImplementationOnce((_b, _callback, error) => error(decodingError))
|
||||
.mockImplementationOnce((_b, callback) => callback(mockAudioBuffer));
|
||||
|
||||
const playback = new Playback(buffer);
|
||||
|
||||
@@ -154,7 +150,7 @@ describe('Playback', () => {
|
||||
expect(playback.currentState).toEqual(PlaybackState.Stopped);
|
||||
});
|
||||
|
||||
it('does not try to re-decode audio', async () => {
|
||||
it("does not try to re-decode audio", async () => {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const playback = new Playback(buffer);
|
||||
await playback.prepare();
|
||||
|
||||
@@ -57,10 +57,7 @@ describe("VoiceMessageRecording", () => {
|
||||
liveData: jest.fn(),
|
||||
amplitudes: testAmplitudes,
|
||||
} as unknown as VoiceRecording;
|
||||
voiceMessageRecording = new VoiceMessageRecording(
|
||||
client,
|
||||
voiceRecording,
|
||||
);
|
||||
voiceMessageRecording = new VoiceMessageRecording(client, voiceRecording);
|
||||
});
|
||||
|
||||
it("hasRecording should return false", () => {
|
||||
@@ -120,9 +117,7 @@ describe("VoiceMessageRecording", () => {
|
||||
});
|
||||
|
||||
it("upload should raise an error", async () => {
|
||||
await expect(voiceMessageRecording.upload(roomId))
|
||||
.rejects
|
||||
.toThrow("No recording available to upload");
|
||||
await expect(voiceMessageRecording.upload(roomId)).rejects.toThrow("No recording available to upload");
|
||||
});
|
||||
|
||||
describe("when the first data has been received", () => {
|
||||
@@ -157,21 +152,23 @@ describe("VoiceMessageRecording", () => {
|
||||
uploadFileRoomId = null;
|
||||
uploadBlob = null;
|
||||
|
||||
mocked(uploadFile).mockImplementation((
|
||||
matrixClient: MatrixClient,
|
||||
roomId: string,
|
||||
file: File | Blob,
|
||||
_progressHandler?: UploadOpts["progressHandler"],
|
||||
): Promise<{ url?: string, file?: IEncryptedFile }> => {
|
||||
uploadFileClient = matrixClient;
|
||||
uploadFileRoomId = roomId;
|
||||
uploadBlob = file;
|
||||
// @ts-ignore
|
||||
return Promise.resolve({
|
||||
url: uploadUrl,
|
||||
file: encryptedFile,
|
||||
});
|
||||
});
|
||||
mocked(uploadFile).mockImplementation(
|
||||
(
|
||||
matrixClient: MatrixClient,
|
||||
roomId: string,
|
||||
file: File | Blob,
|
||||
_progressHandler?: UploadOpts["progressHandler"],
|
||||
): Promise<{ url?: string; file?: IEncryptedFile }> => {
|
||||
uploadFileClient = matrixClient;
|
||||
uploadFileRoomId = roomId;
|
||||
uploadBlob = file;
|
||||
// @ts-ignore
|
||||
return Promise.resolve({
|
||||
url: uploadUrl,
|
||||
file: encryptedFile,
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should upload the file and trigger the upload events", async () => {
|
||||
|
||||
@@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { mocked } from 'jest-mock';
|
||||
import { mocked } from "jest-mock";
|
||||
// @ts-ignore
|
||||
import Recorder from 'opus-recorder/dist/recorder.min.js';
|
||||
import Recorder from "opus-recorder/dist/recorder.min.js";
|
||||
|
||||
import { VoiceRecording, voiceRecorderOptions, highQualityRecorderOptions } from "../../src/audio/VoiceRecording";
|
||||
import { createAudioContext } from '../..//src/audio/compat';
|
||||
import { createAudioContext } from "../..//src/audio/compat";
|
||||
import MediaDeviceHandler from "../../src/MediaDeviceHandler";
|
||||
|
||||
jest.mock('opus-recorder/dist/recorder.min.js');
|
||||
jest.mock("opus-recorder/dist/recorder.min.js");
|
||||
const RecorderMock = mocked(Recorder);
|
||||
|
||||
jest.mock('../../src/audio/compat', () => ({
|
||||
jest.mock("../../src/audio/compat", () => ({
|
||||
createAudioContext: jest.fn(),
|
||||
}));
|
||||
const createAudioContextMock = mocked(createAudioContext);
|
||||
@@ -97,26 +97,34 @@ describe("VoiceRecording", () => {
|
||||
MediaDeviceHandlerMock.getAudioNoiseSuppression.mockReturnValue(false);
|
||||
await recording.start();
|
||||
|
||||
expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith(expect.objectContaining({
|
||||
audio: expect.objectContaining({ noiseSuppression: { ideal: false } }),
|
||||
}));
|
||||
expect(RecorderMock).toHaveBeenCalledWith(expect.objectContaining({
|
||||
encoderBitRate: highQualityRecorderOptions.bitrate,
|
||||
encoderApplication: highQualityRecorderOptions.encoderApplication,
|
||||
}));
|
||||
expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
audio: expect.objectContaining({ noiseSuppression: { ideal: false } }),
|
||||
}),
|
||||
);
|
||||
expect(RecorderMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
encoderBitRate: highQualityRecorderOptions.bitrate,
|
||||
encoderApplication: highQualityRecorderOptions.encoderApplication,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should record normal-quality voice if voice processing is enabled", async () => {
|
||||
MediaDeviceHandlerMock.getAudioNoiseSuppression.mockReturnValue(true);
|
||||
await recording.start();
|
||||
|
||||
expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith(expect.objectContaining({
|
||||
audio: expect.objectContaining({ noiseSuppression: { ideal: true } }),
|
||||
}));
|
||||
expect(RecorderMock).toHaveBeenCalledWith(expect.objectContaining({
|
||||
encoderBitRate: voiceRecorderOptions.bitrate,
|
||||
encoderApplication: voiceRecorderOptions.encoderApplication,
|
||||
}));
|
||||
expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
audio: expect.objectContaining({ noiseSuppression: { ideal: true } }),
|
||||
}),
|
||||
);
|
||||
expect(RecorderMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
encoderBitRate: voiceRecorderOptions.bitrate,
|
||||
encoderApplication: voiceRecorderOptions.encoderApplication,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import EmojiProvider from '../../src/autocomplete/EmojiProvider';
|
||||
import { mkStubRoom } from '../test-utils/test-utils';
|
||||
import EmojiProvider from "../../src/autocomplete/EmojiProvider";
|
||||
import { mkStubRoom } from "../test-utils/test-utils";
|
||||
import { add } from "../../src/emojipicker/recent";
|
||||
import { stubClient } from "../test-utils";
|
||||
import { MatrixClientPeg } from '../../src/MatrixClientPeg';
|
||||
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
|
||||
|
||||
const EMOJI_SHORTCODES = [
|
||||
":+1",
|
||||
@@ -39,20 +39,18 @@ const EMOJI_SHORTCODES = [
|
||||
// Some emoji shortcodes are too short and do not actually trigger autocompletion until the ending `:`.
|
||||
// This means that we cannot compare their autocompletion before and after the ending `:` and have
|
||||
// to simply assert that the final completion with the colon is the exact emoji.
|
||||
const TOO_SHORT_EMOJI_SHORTCODE = [
|
||||
{ emojiShortcode: ":o", expectedEmoji: "⭕️" },
|
||||
];
|
||||
const TOO_SHORT_EMOJI_SHORTCODE = [{ emojiShortcode: ":o", expectedEmoji: "⭕️" }];
|
||||
|
||||
describe('EmojiProvider', function() {
|
||||
describe("EmojiProvider", function () {
|
||||
const testRoom = mkStubRoom(undefined, undefined, undefined);
|
||||
stubClient();
|
||||
MatrixClientPeg.get();
|
||||
|
||||
it.each(EMOJI_SHORTCODES)('Returns consistent results after final colon %s', async function(emojiShortcode) {
|
||||
it.each(EMOJI_SHORTCODES)("Returns consistent results after final colon %s", async function (emojiShortcode) {
|
||||
const ep = new EmojiProvider(testRoom);
|
||||
const range = { "beginning": true, "start": 0, "end": 3 };
|
||||
const range = { beginning: true, start: 0, end: 3 };
|
||||
const completionsBeforeColon = await ep.getCompletions(emojiShortcode, range);
|
||||
const completionsAfterColon = await ep.getCompletions(emojiShortcode + ':', range);
|
||||
const completionsAfterColon = await ep.getCompletions(emojiShortcode + ":", range);
|
||||
|
||||
const firstCompletionWithoutColon = completionsBeforeColon[0].completion;
|
||||
const firstCompletionWithColon = completionsAfterColon[0].completion;
|
||||
@@ -60,17 +58,18 @@ describe('EmojiProvider', function() {
|
||||
expect(firstCompletionWithoutColon).toEqual(firstCompletionWithColon);
|
||||
});
|
||||
|
||||
it.each(
|
||||
TOO_SHORT_EMOJI_SHORTCODE,
|
||||
)('Returns correct results after final colon $emojiShortcode', async ({ emojiShortcode, expectedEmoji }) => {
|
||||
const ep = new EmojiProvider(testRoom);
|
||||
const range = { "beginning": true, "start": 0, "end": 3 };
|
||||
const completions = await ep.getCompletions(emojiShortcode + ':', range);
|
||||
it.each(TOO_SHORT_EMOJI_SHORTCODE)(
|
||||
"Returns correct results after final colon $emojiShortcode",
|
||||
async ({ emojiShortcode, expectedEmoji }) => {
|
||||
const ep = new EmojiProvider(testRoom);
|
||||
const range = { beginning: true, start: 0, end: 3 };
|
||||
const completions = await ep.getCompletions(emojiShortcode + ":", range);
|
||||
|
||||
expect(completions[0].completion).toEqual(expectedEmoji);
|
||||
});
|
||||
expect(completions[0].completion).toEqual(expectedEmoji);
|
||||
},
|
||||
);
|
||||
|
||||
it('Returns correct autocompletion based on recently used emoji', async function() {
|
||||
it("Returns correct autocompletion based on recently used emoji", async function () {
|
||||
add("😘"); //kissing_heart
|
||||
add("😘");
|
||||
add("😚"); //kissing_closed_eyes
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import QueryMatcher from '../../src/autocomplete/QueryMatcher';
|
||||
import QueryMatcher from "../../src/autocomplete/QueryMatcher";
|
||||
|
||||
const OBJECTS = [
|
||||
{ name: "Mel B", nick: "Scary" },
|
||||
@@ -24,160 +24,151 @@ const OBJECTS = [
|
||||
{ name: "Victoria", nick: "Posh" },
|
||||
];
|
||||
|
||||
const NONWORDOBJECTS = [
|
||||
{ name: "B.O.B" },
|
||||
{ name: "bob" },
|
||||
];
|
||||
const NONWORDOBJECTS = [{ name: "B.O.B" }, { name: "bob" }];
|
||||
|
||||
describe('QueryMatcher', function() {
|
||||
it('Returns results by key', function() {
|
||||
describe("QueryMatcher", function () {
|
||||
it("Returns results by key", function () {
|
||||
const qm = new QueryMatcher(OBJECTS, { keys: ["name"] });
|
||||
const results = qm.match('Geri');
|
||||
const results = qm.match("Geri");
|
||||
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].name).toBe('Geri');
|
||||
expect(results[0].name).toBe("Geri");
|
||||
});
|
||||
|
||||
it('Returns results by prefix', function() {
|
||||
it("Returns results by prefix", function () {
|
||||
const qm = new QueryMatcher(OBJECTS, { keys: ["name"] });
|
||||
const results = qm.match('Ge');
|
||||
const results = qm.match("Ge");
|
||||
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].name).toBe('Geri');
|
||||
expect(results[0].name).toBe("Geri");
|
||||
});
|
||||
|
||||
it('Matches case-insensitive', function() {
|
||||
it("Matches case-insensitive", function () {
|
||||
const qm = new QueryMatcher(OBJECTS, { keys: ["name"] });
|
||||
const results = qm.match('geri');
|
||||
const results = qm.match("geri");
|
||||
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].name).toBe('Geri');
|
||||
expect(results[0].name).toBe("Geri");
|
||||
});
|
||||
|
||||
it('Matches ignoring accents', function() {
|
||||
it("Matches ignoring accents", function () {
|
||||
const qm = new QueryMatcher([{ name: "Gëri", foo: 46 }], { keys: ["name"] });
|
||||
const results = qm.match('geri');
|
||||
const results = qm.match("geri");
|
||||
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].foo).toBe(46);
|
||||
});
|
||||
|
||||
it('Returns multiple results in order of search string appearance', function() {
|
||||
it("Returns multiple results in order of search string appearance", function () {
|
||||
const qm = new QueryMatcher(OBJECTS, { keys: ["name", "nick"] });
|
||||
const results = qm.match('or');
|
||||
const results = qm.match("or");
|
||||
|
||||
expect(results.length).toBe(2);
|
||||
expect(results[0].name).toBe('Mel C');
|
||||
expect(results[1].name).toBe('Victoria');
|
||||
expect(results[0].name).toBe("Mel C");
|
||||
expect(results[1].name).toBe("Victoria");
|
||||
|
||||
qm.setObjects(OBJECTS.slice().reverse());
|
||||
const reverseResults = qm.match('or');
|
||||
const reverseResults = qm.match("or");
|
||||
|
||||
// should still be in the same order: search string position
|
||||
// takes precedence over input order
|
||||
expect(reverseResults.length).toBe(2);
|
||||
expect(reverseResults[0].name).toBe('Mel C');
|
||||
expect(reverseResults[1].name).toBe('Victoria');
|
||||
expect(reverseResults[0].name).toBe("Mel C");
|
||||
expect(reverseResults[1].name).toBe("Victoria");
|
||||
});
|
||||
|
||||
it('Returns results with search string in same place according to key index', function() {
|
||||
it("Returns results with search string in same place according to key index", function () {
|
||||
const objects = [
|
||||
{ name: "a", first: "hit", second: "miss", third: "miss" },
|
||||
{ name: "b", first: "miss", second: "hit", third: "miss" },
|
||||
{ name: "c", first: "miss", second: "miss", third: "hit" },
|
||||
];
|
||||
const qm = new QueryMatcher(objects, { keys: ["second", "first", "third"] });
|
||||
const results = qm.match('hit');
|
||||
const results = qm.match("hit");
|
||||
|
||||
expect(results.length).toBe(3);
|
||||
expect(results[0].name).toBe('b');
|
||||
expect(results[1].name).toBe('a');
|
||||
expect(results[2].name).toBe('c');
|
||||
expect(results[0].name).toBe("b");
|
||||
expect(results[1].name).toBe("a");
|
||||
expect(results[2].name).toBe("c");
|
||||
|
||||
qm.setObjects(objects.slice().reverse());
|
||||
|
||||
const reverseResults = qm.match('hit');
|
||||
const reverseResults = qm.match("hit");
|
||||
|
||||
// should still be in the same order: key index
|
||||
// takes precedence over input order
|
||||
expect(reverseResults.length).toBe(3);
|
||||
expect(reverseResults[0].name).toBe('b');
|
||||
expect(reverseResults[1].name).toBe('a');
|
||||
expect(reverseResults[2].name).toBe('c');
|
||||
expect(reverseResults[0].name).toBe("b");
|
||||
expect(reverseResults[1].name).toBe("a");
|
||||
expect(reverseResults[2].name).toBe("c");
|
||||
});
|
||||
|
||||
it('Returns results with search string in same place and key in same place in insertion order', function() {
|
||||
it("Returns results with search string in same place and key in same place in insertion order", function () {
|
||||
const qm = new QueryMatcher(OBJECTS, { keys: ["name"] });
|
||||
const results = qm.match('Mel');
|
||||
const results = qm.match("Mel");
|
||||
|
||||
expect(results.length).toBe(2);
|
||||
expect(results[0].name).toBe('Mel B');
|
||||
expect(results[1].name).toBe('Mel C');
|
||||
expect(results[0].name).toBe("Mel B");
|
||||
expect(results[1].name).toBe("Mel C");
|
||||
|
||||
qm.setObjects(OBJECTS.slice().reverse());
|
||||
|
||||
const reverseResults = qm.match('Mel');
|
||||
const reverseResults = qm.match("Mel");
|
||||
|
||||
expect(reverseResults.length).toBe(2);
|
||||
expect(reverseResults[0].name).toBe('Mel C');
|
||||
expect(reverseResults[1].name).toBe('Mel B');
|
||||
expect(reverseResults[0].name).toBe("Mel C");
|
||||
expect(reverseResults[1].name).toBe("Mel B");
|
||||
});
|
||||
|
||||
it('Returns numeric results in correct order (input pos)', function() {
|
||||
it("Returns numeric results in correct order (input pos)", function () {
|
||||
// regression test for depending on object iteration order
|
||||
const qm = new QueryMatcher([
|
||||
{ name: "123456badger" },
|
||||
{ name: "123456" },
|
||||
], { keys: ["name"] });
|
||||
const results = qm.match('123456');
|
||||
const qm = new QueryMatcher([{ name: "123456badger" }, { name: "123456" }], { keys: ["name"] });
|
||||
const results = qm.match("123456");
|
||||
|
||||
expect(results.length).toBe(2);
|
||||
expect(results[0].name).toBe('123456badger');
|
||||
expect(results[1].name).toBe('123456');
|
||||
expect(results[0].name).toBe("123456badger");
|
||||
expect(results[1].name).toBe("123456");
|
||||
});
|
||||
|
||||
it('Returns numeric results in correct order (query pos)', function() {
|
||||
const qm = new QueryMatcher([
|
||||
{ name: "999999123456" },
|
||||
{ name: "123456badger" },
|
||||
], { keys: ["name"] });
|
||||
const results = qm.match('123456');
|
||||
it("Returns numeric results in correct order (query pos)", function () {
|
||||
const qm = new QueryMatcher([{ name: "999999123456" }, { name: "123456badger" }], { keys: ["name"] });
|
||||
const results = qm.match("123456");
|
||||
|
||||
expect(results.length).toBe(2);
|
||||
expect(results[0].name).toBe('123456badger');
|
||||
expect(results[1].name).toBe('999999123456');
|
||||
expect(results[0].name).toBe("123456badger");
|
||||
expect(results[1].name).toBe("999999123456");
|
||||
});
|
||||
|
||||
it('Returns results by function', function() {
|
||||
it("Returns results by function", function () {
|
||||
const qm = new QueryMatcher(OBJECTS, {
|
||||
keys: ["name"],
|
||||
funcs: [x => x.name.replace('Mel', 'Emma')],
|
||||
funcs: [(x) => x.name.replace("Mel", "Emma")],
|
||||
});
|
||||
|
||||
const results = qm.match('Emma');
|
||||
const results = qm.match("Emma");
|
||||
expect(results.length).toBe(3);
|
||||
expect(results[0].name).toBe('Emma');
|
||||
expect(results[1].name).toBe('Mel B');
|
||||
expect(results[2].name).toBe('Mel C');
|
||||
expect(results[0].name).toBe("Emma");
|
||||
expect(results[1].name).toBe("Mel B");
|
||||
expect(results[2].name).toBe("Mel C");
|
||||
});
|
||||
|
||||
it('Matches words only by default', function() {
|
||||
it("Matches words only by default", function () {
|
||||
const qm = new QueryMatcher(NONWORDOBJECTS, { keys: ["name"] });
|
||||
|
||||
const results = qm.match('bob');
|
||||
const results = qm.match("bob");
|
||||
expect(results.length).toBe(2);
|
||||
expect(results[0].name).toBe('B.O.B');
|
||||
expect(results[1].name).toBe('bob');
|
||||
expect(results[0].name).toBe("B.O.B");
|
||||
expect(results[1].name).toBe("bob");
|
||||
});
|
||||
|
||||
it('Matches all chars with words-only off', function() {
|
||||
it("Matches all chars with words-only off", function () {
|
||||
const qm = new QueryMatcher(NONWORDOBJECTS, {
|
||||
keys: ["name"],
|
||||
shouldMatchWordsOnly: false,
|
||||
});
|
||||
|
||||
const results = qm.match('bob');
|
||||
const results = qm.match("bob");
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].name).toBe('bob');
|
||||
expect(results[0].name).toBe("bob");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,43 +14,44 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { screen, render, fireEvent, waitFor, within, act } from '@testing-library/react';
|
||||
import React from "react";
|
||||
import { screen, render, fireEvent, waitFor, within, act } from "@testing-library/react";
|
||||
|
||||
import * as TestUtils from '../../test-utils';
|
||||
import AutocompleteProvider from '../../../src/autocomplete/AutocompleteProvider';
|
||||
import { ICompletion } from '../../../src/autocomplete/Autocompleter';
|
||||
import * as TestUtils from "../../test-utils";
|
||||
import AutocompleteProvider from "../../../src/autocomplete/AutocompleteProvider";
|
||||
import { ICompletion } from "../../../src/autocomplete/Autocompleter";
|
||||
import { AutocompleteInput } from "../../../src/components/structures/AutocompleteInput";
|
||||
|
||||
describe('AutocompleteInput', () => {
|
||||
describe("AutocompleteInput", () => {
|
||||
const mockCompletion: ICompletion[] = [
|
||||
{ type: 'user', completion: 'user_1', completionId: '@user_1:host.local', range: { start: 1, end: 1 } },
|
||||
{ type: 'user', completion: 'user_2', completionId: '@user_2:host.local', range: { start: 1, end: 1 } },
|
||||
{ type: "user", completion: "user_1", completionId: "@user_1:host.local", range: { start: 1, end: 1 } },
|
||||
{ type: "user", completion: "user_2", completionId: "@user_2:host.local", range: { start: 1, end: 1 } },
|
||||
];
|
||||
|
||||
const constructMockProvider = (data: ICompletion[]) => ({
|
||||
getCompletions: jest.fn().mockImplementation(async () => data),
|
||||
}) as unknown as AutocompleteProvider;
|
||||
const constructMockProvider = (data: ICompletion[]) =>
|
||||
({
|
||||
getCompletions: jest.fn().mockImplementation(async () => data),
|
||||
} as unknown as AutocompleteProvider);
|
||||
|
||||
beforeEach(() => {
|
||||
TestUtils.stubClient();
|
||||
});
|
||||
|
||||
const getEditorInput = () => {
|
||||
const input = screen.getByTestId('autocomplete-input');
|
||||
const input = screen.getByTestId("autocomplete-input");
|
||||
expect(input).toBeDefined();
|
||||
|
||||
return input;
|
||||
};
|
||||
|
||||
it('should render suggestions when a query is set', async () => {
|
||||
it("should render suggestions when a query is set", async () => {
|
||||
const mockProvider = constructMockProvider(mockCompletion);
|
||||
const onSelectionChangeMock = jest.fn();
|
||||
|
||||
render(
|
||||
<AutocompleteInput
|
||||
provider={mockProvider}
|
||||
placeholder='Search ...'
|
||||
placeholder="Search ..."
|
||||
selection={[]}
|
||||
onSelectionChange={onSelectionChangeMock}
|
||||
/>,
|
||||
@@ -60,45 +61,45 @@ describe('AutocompleteInput', () => {
|
||||
|
||||
act(() => {
|
||||
fireEvent.focus(input);
|
||||
fireEvent.change(input, { target: { value: 'user' } });
|
||||
fireEvent.change(input, { target: { value: "user" } });
|
||||
});
|
||||
|
||||
await waitFor(() => expect(mockProvider.getCompletions).toHaveBeenCalledTimes(1));
|
||||
expect(screen.getByTestId('autocomplete-matches').childNodes).toHaveLength(mockCompletion.length);
|
||||
expect(screen.getByTestId("autocomplete-matches").childNodes).toHaveLength(mockCompletion.length);
|
||||
});
|
||||
|
||||
it('should render selected items passed in via props', () => {
|
||||
it("should render selected items passed in via props", () => {
|
||||
const mockProvider = constructMockProvider(mockCompletion);
|
||||
const onSelectionChangeMock = jest.fn();
|
||||
|
||||
render(
|
||||
<AutocompleteInput
|
||||
provider={mockProvider}
|
||||
placeholder='Search ...'
|
||||
placeholder="Search ..."
|
||||
selection={mockCompletion}
|
||||
onSelectionChange={onSelectionChangeMock}
|
||||
/>,
|
||||
);
|
||||
|
||||
const editor = screen.getByTestId('autocomplete-editor');
|
||||
const editor = screen.getByTestId("autocomplete-editor");
|
||||
const selection = within(editor).getAllByTestId("autocomplete-selection-item", { exact: false });
|
||||
expect(selection).toHaveLength(mockCompletion.length);
|
||||
});
|
||||
|
||||
it('should call onSelectionChange() when an item is removed from selection', () => {
|
||||
it("should call onSelectionChange() when an item is removed from selection", () => {
|
||||
const mockProvider = constructMockProvider(mockCompletion);
|
||||
const onSelectionChangeMock = jest.fn();
|
||||
|
||||
render(
|
||||
<AutocompleteInput
|
||||
provider={mockProvider}
|
||||
placeholder='Search ...'
|
||||
placeholder="Search ..."
|
||||
selection={mockCompletion}
|
||||
onSelectionChange={onSelectionChangeMock}
|
||||
/>,
|
||||
);
|
||||
|
||||
const editor = screen.getByTestId('autocomplete-editor');
|
||||
const editor = screen.getByTestId("autocomplete-editor");
|
||||
const removeButtons = within(editor).getAllByTestId("autocomplete-selection-remove-button", { exact: false });
|
||||
expect(removeButtons).toHaveLength(mockCompletion.length);
|
||||
|
||||
@@ -110,39 +111,35 @@ describe('AutocompleteInput', () => {
|
||||
expect(onSelectionChangeMock).toHaveBeenCalledWith([mockCompletion[1]]);
|
||||
});
|
||||
|
||||
it('should render custom selection element when renderSelection() is defined', () => {
|
||||
it("should render custom selection element when renderSelection() is defined", () => {
|
||||
const mockProvider = constructMockProvider(mockCompletion);
|
||||
const onSelectionChangeMock = jest.fn();
|
||||
|
||||
const renderSelection = () => (
|
||||
<span data-testid='custom-selection-element'>custom selection element</span>
|
||||
);
|
||||
const renderSelection = () => <span data-testid="custom-selection-element">custom selection element</span>;
|
||||
|
||||
render(
|
||||
<AutocompleteInput
|
||||
provider={mockProvider}
|
||||
placeholder='Search ...'
|
||||
placeholder="Search ..."
|
||||
selection={mockCompletion}
|
||||
onSelectionChange={onSelectionChangeMock}
|
||||
renderSelection={renderSelection}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getAllByTestId('custom-selection-element')).toHaveLength(mockCompletion.length);
|
||||
expect(screen.getAllByTestId("custom-selection-element")).toHaveLength(mockCompletion.length);
|
||||
});
|
||||
|
||||
it('should render custom suggestion element when renderSuggestion() is defined', async () => {
|
||||
it("should render custom suggestion element when renderSuggestion() is defined", async () => {
|
||||
const mockProvider = constructMockProvider(mockCompletion);
|
||||
const onSelectionChangeMock = jest.fn();
|
||||
|
||||
const renderSuggestion = () => (
|
||||
<span data-testid='custom-suggestion-element'>custom suggestion element</span>
|
||||
);
|
||||
const renderSuggestion = () => <span data-testid="custom-suggestion-element">custom suggestion element</span>;
|
||||
|
||||
render(
|
||||
<AutocompleteInput
|
||||
provider={mockProvider}
|
||||
placeholder='Search ...'
|
||||
placeholder="Search ..."
|
||||
selection={mockCompletion}
|
||||
onSelectionChange={onSelectionChangeMock}
|
||||
renderSuggestion={renderSuggestion}
|
||||
@@ -153,21 +150,21 @@ describe('AutocompleteInput', () => {
|
||||
|
||||
act(() => {
|
||||
fireEvent.focus(input);
|
||||
fireEvent.change(input, { target: { value: 'user' } });
|
||||
fireEvent.change(input, { target: { value: "user" } });
|
||||
});
|
||||
|
||||
await waitFor(() => expect(mockProvider.getCompletions).toHaveBeenCalledTimes(1));
|
||||
expect(screen.getAllByTestId('custom-suggestion-element')).toHaveLength(mockCompletion.length);
|
||||
expect(screen.getAllByTestId("custom-suggestion-element")).toHaveLength(mockCompletion.length);
|
||||
});
|
||||
|
||||
it('should mark selected suggestions as selected', async () => {
|
||||
it("should mark selected suggestions as selected", async () => {
|
||||
const mockProvider = constructMockProvider(mockCompletion);
|
||||
const onSelectionChangeMock = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<AutocompleteInput
|
||||
provider={mockProvider}
|
||||
placeholder='Search ...'
|
||||
placeholder="Search ..."
|
||||
selection={mockCompletion}
|
||||
onSelectionChange={onSelectionChangeMock}
|
||||
/>,
|
||||
@@ -177,23 +174,23 @@ describe('AutocompleteInput', () => {
|
||||
|
||||
act(() => {
|
||||
fireEvent.focus(input);
|
||||
fireEvent.change(input, { target: { value: 'user' } });
|
||||
fireEvent.change(input, { target: { value: "user" } });
|
||||
});
|
||||
|
||||
await waitFor(() => expect(mockProvider.getCompletions).toHaveBeenCalledTimes(1));
|
||||
const suggestions = await within(container).findAllByTestId('autocomplete-suggestion-item', { exact: false });
|
||||
const suggestions = await within(container).findAllByTestId("autocomplete-suggestion-item", { exact: false });
|
||||
expect(suggestions).toHaveLength(mockCompletion.length);
|
||||
suggestions.map(suggestion => expect(suggestion).toHaveClass('mx_AutocompleteInput_suggestion--selected'));
|
||||
suggestions.map((suggestion) => expect(suggestion).toHaveClass("mx_AutocompleteInput_suggestion--selected"));
|
||||
});
|
||||
|
||||
it('should remove the last added selection when backspace is pressed in empty input', () => {
|
||||
it("should remove the last added selection when backspace is pressed in empty input", () => {
|
||||
const mockProvider = constructMockProvider(mockCompletion);
|
||||
const onSelectionChangeMock = jest.fn();
|
||||
|
||||
render(
|
||||
<AutocompleteInput
|
||||
provider={mockProvider}
|
||||
placeholder='Search ...'
|
||||
placeholder="Search ..."
|
||||
selection={mockCompletion}
|
||||
onSelectionChange={onSelectionChangeMock}
|
||||
/>,
|
||||
@@ -202,20 +199,20 @@ describe('AutocompleteInput', () => {
|
||||
const input = getEditorInput();
|
||||
|
||||
act(() => {
|
||||
fireEvent.keyDown(input, { key: 'Backspace' });
|
||||
fireEvent.keyDown(input, { key: "Backspace" });
|
||||
});
|
||||
|
||||
expect(onSelectionChangeMock).toHaveBeenCalledWith([mockCompletion[0]]);
|
||||
});
|
||||
|
||||
it('should toggle a selected item when a suggestion is clicked', async () => {
|
||||
it("should toggle a selected item when a suggestion is clicked", async () => {
|
||||
const mockProvider = constructMockProvider(mockCompletion);
|
||||
const onSelectionChangeMock = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<AutocompleteInput
|
||||
provider={mockProvider}
|
||||
placeholder='Search ...'
|
||||
placeholder="Search ..."
|
||||
selection={[]}
|
||||
onSelectionChange={onSelectionChangeMock}
|
||||
/>,
|
||||
@@ -225,10 +222,10 @@ describe('AutocompleteInput', () => {
|
||||
|
||||
act(() => {
|
||||
fireEvent.focus(input);
|
||||
fireEvent.change(input, { target: { value: 'user' } });
|
||||
fireEvent.change(input, { target: { value: "user" } });
|
||||
});
|
||||
|
||||
const suggestions = await within(container).findAllByTestId('autocomplete-suggestion-item', { exact: false });
|
||||
const suggestions = await within(container).findAllByTestId("autocomplete-suggestion-item", { exact: false });
|
||||
|
||||
act(() => {
|
||||
fireEvent.mouseDown(suggestions[0]);
|
||||
|
||||
@@ -85,4 +85,3 @@ describe("ContextMenu", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixClient, MatrixEvent } from 'matrix-js-sdk/src/matrix';
|
||||
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
import { CallState } from "matrix-js-sdk/src/webrtc/call";
|
||||
|
||||
import { stubClient } from '../../test-utils';
|
||||
import { MatrixClientPeg } from '../../../src/MatrixClientPeg';
|
||||
import { stubClient } from "../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
||||
import LegacyCallEventGrouper, { CustomCallState } from "../../../src/components/structures/LegacyCallEventGrouper";
|
||||
|
||||
const MY_USER_ID = "@me:here";
|
||||
@@ -27,7 +27,7 @@ const THEIR_USER_ID = "@they:here";
|
||||
|
||||
let client: MatrixClient;
|
||||
|
||||
describe('LegacyCallEventGrouper', () => {
|
||||
describe("LegacyCallEventGrouper", () => {
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
client = MatrixClientPeg.get();
|
||||
|
||||
@@ -15,13 +15,13 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { EventEmitter } from "events";
|
||||
import { Room, RoomMember } from 'matrix-js-sdk/src/matrix';
|
||||
import FakeTimers from '@sinonjs/fake-timers';
|
||||
import { render } from '@testing-library/react';
|
||||
import { Thread } from 'matrix-js-sdk/src/models/thread';
|
||||
import { Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
import FakeTimers from "@sinonjs/fake-timers";
|
||||
import { render } from "@testing-library/react";
|
||||
import { Thread } from "matrix-js-sdk/src/models/thread";
|
||||
|
||||
import MessagePanel, { shouldFormContinuation } from "../../../src/components/structures/MessagePanel";
|
||||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
@@ -34,21 +34,21 @@ import {
|
||||
makeBeaconInfoEvent,
|
||||
mockClientMethodsEvents,
|
||||
mockClientMethodsUser,
|
||||
} from '../../test-utils';
|
||||
import ResizeNotifier from '../../../src/utils/ResizeNotifier';
|
||||
import { IRoomState } from '../../../src/components/structures/RoomView';
|
||||
} from "../../test-utils";
|
||||
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
|
||||
import { IRoomState } from "../../../src/components/structures/RoomView";
|
||||
|
||||
jest.mock('../../../src/utils/beacon', () => ({
|
||||
jest.mock("../../../src/utils/beacon", () => ({
|
||||
useBeacon: jest.fn(),
|
||||
}));
|
||||
|
||||
const roomId = "!roomId:server_name";
|
||||
|
||||
describe('MessagePanel', function() {
|
||||
describe("MessagePanel", function () {
|
||||
let clock = null;
|
||||
const realSetTimeout = window.setTimeout;
|
||||
const events = mkEvents();
|
||||
const userId = '@me:here';
|
||||
const userId = "@me:here";
|
||||
const client = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
...mockClientMethodsEvents(),
|
||||
@@ -61,22 +61,22 @@ describe('MessagePanel', function() {
|
||||
|
||||
const room = new Room(roomId, client, userId);
|
||||
|
||||
const bobMember = new RoomMember(roomId, '@bob:id');
|
||||
bobMember.name = 'Bob';
|
||||
jest.spyOn(bobMember, 'getAvatarUrl').mockReturnValue('avatar.jpeg');
|
||||
jest.spyOn(bobMember, 'getMxcAvatarUrl').mockReturnValue('mxc://avatar.url/image.png');
|
||||
const bobMember = new RoomMember(roomId, "@bob:id");
|
||||
bobMember.name = "Bob";
|
||||
jest.spyOn(bobMember, "getAvatarUrl").mockReturnValue("avatar.jpeg");
|
||||
jest.spyOn(bobMember, "getMxcAvatarUrl").mockReturnValue("mxc://avatar.url/image.png");
|
||||
|
||||
const alice = "@alice:example.org";
|
||||
const aliceMember = new RoomMember(roomId, alice);
|
||||
aliceMember.name = 'Alice';
|
||||
jest.spyOn(aliceMember, 'getAvatarUrl').mockReturnValue('avatar.jpeg');
|
||||
jest.spyOn(aliceMember, 'getMxcAvatarUrl').mockReturnValue('mxc://avatar.url/image.png');
|
||||
aliceMember.name = "Alice";
|
||||
jest.spyOn(aliceMember, "getAvatarUrl").mockReturnValue("avatar.jpeg");
|
||||
jest.spyOn(aliceMember, "getMxcAvatarUrl").mockReturnValue("mxc://avatar.url/image.png");
|
||||
|
||||
const defaultProps = {
|
||||
resizeNotifier: new EventEmitter as unknown as ResizeNotifier,
|
||||
resizeNotifier: new EventEmitter() as unknown as ResizeNotifier,
|
||||
callEventGroupers: new Map(),
|
||||
room,
|
||||
className: 'cls',
|
||||
className: "cls",
|
||||
events: [],
|
||||
};
|
||||
|
||||
@@ -95,24 +95,26 @@ describe('MessagePanel', function() {
|
||||
showHiddenEvents: false,
|
||||
} as unknown as IRoomState;
|
||||
|
||||
const getComponent = (props = {}, roomContext: Partial<IRoomState> = {}) =>
|
||||
const getComponent = (props = {}, roomContext: Partial<IRoomState> = {}) => (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={{ ...defaultRoomContext, ...roomContext }}>
|
||||
<MessagePanel {...defaultProps} {...props} />
|
||||
</RoomContext.Provider>);
|
||||
</MatrixClientContext.Provider>;
|
||||
</RoomContext.Provider>
|
||||
);
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
jest.clearAllMocks();
|
||||
// HACK: We assume all settings want to be disabled
|
||||
jest.spyOn(SettingsStore, 'getValue').mockImplementation((arg) => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((arg) => {
|
||||
return arg === "showDisplaynameChanges";
|
||||
});
|
||||
|
||||
DMRoomMap.makeShared();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
afterEach(function () {
|
||||
if (clock) {
|
||||
clock.uninstall();
|
||||
clock = null;
|
||||
@@ -123,11 +125,14 @@ describe('MessagePanel', function() {
|
||||
const events = [];
|
||||
const ts0 = Date.now();
|
||||
for (let i = 0; i < 10; i++) {
|
||||
events.push(TestUtilsMatrix.mkMessage(
|
||||
{
|
||||
event: true, room: "!room:id", user: "@user:id",
|
||||
events.push(
|
||||
TestUtilsMatrix.mkMessage({
|
||||
event: true,
|
||||
room: "!room:id",
|
||||
user: "@user:id",
|
||||
ts: ts0 + i * 1000,
|
||||
}));
|
||||
}),
|
||||
);
|
||||
}
|
||||
return events;
|
||||
}
|
||||
@@ -135,13 +140,16 @@ describe('MessagePanel', function() {
|
||||
// Just to avoid breaking Dateseparator tests that might run at 00hrs
|
||||
function mkOneDayEvents() {
|
||||
const events = [];
|
||||
const ts0 = Date.parse('09 May 2004 00:12:00 GMT');
|
||||
const ts0 = Date.parse("09 May 2004 00:12:00 GMT");
|
||||
for (let i = 0; i < 10; i++) {
|
||||
events.push(TestUtilsMatrix.mkMessage(
|
||||
{
|
||||
event: true, room: "!room:id", user: "@user:id",
|
||||
events.push(
|
||||
TestUtilsMatrix.mkMessage({
|
||||
event: true,
|
||||
room: "!room:id",
|
||||
user: "@user:id",
|
||||
ts: ts0 + i * 1000,
|
||||
}));
|
||||
}),
|
||||
);
|
||||
}
|
||||
return events;
|
||||
}
|
||||
@@ -152,26 +160,38 @@ describe('MessagePanel', function() {
|
||||
const ts0 = Date.now();
|
||||
|
||||
let i = 0;
|
||||
events.push(TestUtilsMatrix.mkMessage({
|
||||
event: true, room: "!room:id", user: "@user:id",
|
||||
ts: ts0 + ++i * 1000,
|
||||
}));
|
||||
events.push(
|
||||
TestUtilsMatrix.mkMessage({
|
||||
event: true,
|
||||
room: "!room:id",
|
||||
user: "@user:id",
|
||||
ts: ts0 + ++i * 1000,
|
||||
}),
|
||||
);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
events.push(TestUtilsMatrix.mkMembership({
|
||||
event: true, room: "!room:id", user: "@user:id",
|
||||
target: bobMember,
|
||||
ts: ts0 + i*1000,
|
||||
mship: 'join',
|
||||
prevMship: 'join',
|
||||
name: 'A user',
|
||||
}));
|
||||
events.push(
|
||||
TestUtilsMatrix.mkMembership({
|
||||
event: true,
|
||||
room: "!room:id",
|
||||
user: "@user:id",
|
||||
target: bobMember,
|
||||
ts: ts0 + i * 1000,
|
||||
mship: "join",
|
||||
prevMship: "join",
|
||||
name: "A user",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
events.push(TestUtilsMatrix.mkMessage({
|
||||
event: true, room: "!room:id", user: "@user:id",
|
||||
ts: ts0 + ++i*1000,
|
||||
}));
|
||||
events.push(
|
||||
TestUtilsMatrix.mkMessage({
|
||||
event: true,
|
||||
room: "!room:id",
|
||||
user: "@user:id",
|
||||
ts: ts0 + ++i * 1000,
|
||||
}),
|
||||
);
|
||||
|
||||
return events;
|
||||
}
|
||||
@@ -184,14 +204,18 @@ describe('MessagePanel', function() {
|
||||
let i = 0;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
events.push(TestUtilsMatrix.mkMembership({
|
||||
event: true, room: "!room:id", user: "@user:id",
|
||||
target: bobMember,
|
||||
ts: ts0 + i * 1000,
|
||||
mship: 'join',
|
||||
prevMship: 'join',
|
||||
name: 'A user',
|
||||
}));
|
||||
events.push(
|
||||
TestUtilsMatrix.mkMembership({
|
||||
event: true,
|
||||
room: "!room:id",
|
||||
user: "@user:id",
|
||||
target: bobMember,
|
||||
ts: ts0 + i * 1000,
|
||||
mship: "join",
|
||||
prevMship: "join",
|
||||
name: "A user",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return events;
|
||||
@@ -227,8 +251,8 @@ describe('MessagePanel', function() {
|
||||
user: alice,
|
||||
target: aliceMember,
|
||||
ts: ts0 + 1,
|
||||
mship: 'join',
|
||||
name: 'Alice',
|
||||
mship: "join",
|
||||
name: "Alice",
|
||||
}),
|
||||
mkEvent({
|
||||
event: true,
|
||||
@@ -236,7 +260,7 @@ describe('MessagePanel', function() {
|
||||
room: roomId,
|
||||
user: alice,
|
||||
content: {
|
||||
"join_rule": "invite",
|
||||
join_rule: "invite",
|
||||
},
|
||||
ts: ts0 + 2,
|
||||
}),
|
||||
@@ -246,7 +270,7 @@ describe('MessagePanel', function() {
|
||||
room: roomId,
|
||||
user: alice,
|
||||
content: {
|
||||
"history_visibility": "invited",
|
||||
history_visibility: "invited",
|
||||
},
|
||||
ts: ts0 + 3,
|
||||
}),
|
||||
@@ -256,7 +280,7 @@ describe('MessagePanel', function() {
|
||||
room: roomId,
|
||||
user: alice,
|
||||
content: {
|
||||
"algorithm": "m.megolm.v1.aes-sha2",
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
},
|
||||
ts: ts0 + 4,
|
||||
}),
|
||||
@@ -267,8 +291,8 @@ describe('MessagePanel', function() {
|
||||
skey: "@bob:example.org",
|
||||
target: bobMember,
|
||||
ts: ts0 + 5,
|
||||
mship: 'invite',
|
||||
name: 'Bob',
|
||||
mship: "invite",
|
||||
name: "Bob",
|
||||
}),
|
||||
];
|
||||
}
|
||||
@@ -300,52 +324,58 @@ describe('MessagePanel', function() {
|
||||
return rmContainer && rmContainer.children.length > 0;
|
||||
}
|
||||
|
||||
it('should show the events', function() {
|
||||
it("should show the events", function () {
|
||||
const { container } = render(getComponent({ events }));
|
||||
|
||||
// just check we have the right number of tiles for now
|
||||
const tiles = container.getElementsByClassName('mx_EventTile');
|
||||
const tiles = container.getElementsByClassName("mx_EventTile");
|
||||
expect(tiles.length).toEqual(10);
|
||||
});
|
||||
|
||||
it('should collapse adjacent member events', function() {
|
||||
it("should collapse adjacent member events", function () {
|
||||
const { container } = render(getComponent({ events: mkMelsEvents() }));
|
||||
|
||||
// just check we have the right number of tiles for now
|
||||
const tiles = container.getElementsByClassName('mx_EventTile');
|
||||
const tiles = container.getElementsByClassName("mx_EventTile");
|
||||
expect(tiles.length).toEqual(2);
|
||||
|
||||
const summaryTiles = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
const summaryTiles = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(summaryTiles.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should insert the read-marker in the right place', function() {
|
||||
const { container } = render(getComponent({
|
||||
events, readMarkerEventId: events[4].getId(), readMarkerVisible: true,
|
||||
}));
|
||||
it("should insert the read-marker in the right place", function () {
|
||||
const { container } = render(
|
||||
getComponent({
|
||||
events,
|
||||
readMarkerEventId: events[4].getId(),
|
||||
readMarkerVisible: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const tiles = container.getElementsByClassName('mx_EventTile');
|
||||
const tiles = container.getElementsByClassName("mx_EventTile");
|
||||
|
||||
// find the <li> which wraps the read marker
|
||||
const [rm] = container.getElementsByClassName('mx_RoomView_myReadMarker_container');
|
||||
const [rm] = container.getElementsByClassName("mx_RoomView_myReadMarker_container");
|
||||
|
||||
// it should follow the <li> which wraps the event tile for event 4
|
||||
const eventContainer = ReactDOM.findDOMNode(tiles[4]);
|
||||
expect(rm.previousSibling).toEqual(eventContainer);
|
||||
});
|
||||
|
||||
it('should show the read-marker that fall in summarised events after the summary', function() {
|
||||
it("should show the read-marker that fall in summarised events after the summary", function () {
|
||||
const melsEvents = mkMelsEvents();
|
||||
const { container } = render(getComponent({
|
||||
events: melsEvents,
|
||||
readMarkerEventId: melsEvents[4].getId(),
|
||||
readMarkerVisible: true,
|
||||
}));
|
||||
const { container } = render(
|
||||
getComponent({
|
||||
events: melsEvents,
|
||||
readMarkerEventId: melsEvents[4].getId(),
|
||||
readMarkerVisible: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const [summary] = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
const [summary] = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
|
||||
// find the <li> which wraps the read marker
|
||||
const [rm] = container.getElementsByClassName('mx_RoomView_myReadMarker_container');
|
||||
const [rm] = container.getElementsByClassName("mx_RoomView_myReadMarker_container");
|
||||
|
||||
expect(rm.previousSibling).toEqual(summary);
|
||||
|
||||
@@ -353,19 +383,21 @@ describe('MessagePanel', function() {
|
||||
expect(isReadMarkerVisible(rm)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should hide the read-marker at the end of summarised events', function() {
|
||||
it("should hide the read-marker at the end of summarised events", function () {
|
||||
const melsEvents = mkMelsEventsOnly();
|
||||
|
||||
const { container } = render(getComponent({
|
||||
events: melsEvents,
|
||||
readMarkerEventId: melsEvents[9].getId(),
|
||||
readMarkerVisible: true,
|
||||
}));
|
||||
const { container } = render(
|
||||
getComponent({
|
||||
events: melsEvents,
|
||||
readMarkerEventId: melsEvents[9].getId(),
|
||||
readMarkerVisible: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const [summary] = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
const [summary] = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
|
||||
// find the <li> which wraps the read marker
|
||||
const [rm] = container.getElementsByClassName('mx_RoomView_myReadMarker_container');
|
||||
const [rm] = container.getElementsByClassName("mx_RoomView_myReadMarker_container");
|
||||
|
||||
expect(rm.previousSibling).toEqual(summary);
|
||||
|
||||
@@ -373,34 +405,38 @@ describe('MessagePanel', function() {
|
||||
expect(isReadMarkerVisible(rm)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('shows a ghost read-marker when the read-marker moves', function(done) {
|
||||
it("shows a ghost read-marker when the read-marker moves", function (done) {
|
||||
// fake the clock so that we can test the velocity animation.
|
||||
clock = FakeTimers.install();
|
||||
|
||||
const { container, rerender } = render(<div>
|
||||
{ getComponent({
|
||||
events,
|
||||
readMarkerEventId: events[4].getId(),
|
||||
readMarkerVisible: true,
|
||||
}) }
|
||||
</div>);
|
||||
const { container, rerender } = render(
|
||||
<div>
|
||||
{getComponent({
|
||||
events,
|
||||
readMarkerEventId: events[4].getId(),
|
||||
readMarkerVisible: true,
|
||||
})}
|
||||
</div>,
|
||||
);
|
||||
|
||||
const tiles = container.getElementsByClassName('mx_EventTile');
|
||||
const tiles = container.getElementsByClassName("mx_EventTile");
|
||||
|
||||
// find the <li> which wraps the read marker
|
||||
const [rm] = container.getElementsByClassName('mx_RoomView_myReadMarker_container');
|
||||
const [rm] = container.getElementsByClassName("mx_RoomView_myReadMarker_container");
|
||||
expect(rm.previousSibling).toEqual(tiles[4]);
|
||||
|
||||
rerender(<div>
|
||||
{ getComponent({
|
||||
events,
|
||||
readMarkerEventId: events[6].getId(),
|
||||
readMarkerVisible: true,
|
||||
}) }
|
||||
</div>);
|
||||
rerender(
|
||||
<div>
|
||||
{getComponent({
|
||||
events,
|
||||
readMarkerEventId: events[6].getId(),
|
||||
readMarkerVisible: true,
|
||||
})}
|
||||
</div>,
|
||||
);
|
||||
|
||||
// now there should be two RM containers
|
||||
const readMarkers = container.getElementsByClassName('mx_RoomView_myReadMarker_container');
|
||||
const readMarkers = container.getElementsByClassName("mx_RoomView_myReadMarker_container");
|
||||
|
||||
expect(readMarkers.length).toEqual(2);
|
||||
|
||||
@@ -420,72 +456,72 @@ describe('MessagePanel', function() {
|
||||
clock.tick(1000);
|
||||
realSetTimeout(() => {
|
||||
// the ghost should now have finished
|
||||
expect(hr.style.opacity).toEqual('0');
|
||||
expect(hr.style.opacity).toEqual("0");
|
||||
done();
|
||||
}, 100);
|
||||
}, 100);
|
||||
});
|
||||
|
||||
it('should collapse creation events', function() {
|
||||
it("should collapse creation events", function () {
|
||||
const events = mkCreationEvents();
|
||||
TestUtilsMatrix.upsertRoomStateEvents(room, events);
|
||||
const { container } = render(getComponent({ events }));
|
||||
|
||||
const createEvent = events.find(event => event.getType() === 'm.room.create');
|
||||
const encryptionEvent = events.find(event => event.getType() === 'm.room.encryption');
|
||||
const createEvent = events.find((event) => event.getType() === "m.room.create");
|
||||
const encryptionEvent = events.find((event) => event.getType() === "m.room.encryption");
|
||||
|
||||
// we expect that
|
||||
// - the room creation event, the room encryption event, and Alice inviting Bob,
|
||||
// should be outside of the room creation summary
|
||||
// - all other events should be inside the room creation summary
|
||||
|
||||
const tiles = container.getElementsByClassName('mx_EventTile');
|
||||
const tiles = container.getElementsByClassName("mx_EventTile");
|
||||
|
||||
expect(tiles[0].getAttribute('data-event-id')).toEqual(createEvent.getId());
|
||||
expect(tiles[1].getAttribute('data-event-id')).toEqual(encryptionEvent.getId());
|
||||
expect(tiles[0].getAttribute("data-event-id")).toEqual(createEvent.getId());
|
||||
expect(tiles[1].getAttribute("data-event-id")).toEqual(encryptionEvent.getId());
|
||||
|
||||
const [summaryTile] = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
const [summaryTile] = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
|
||||
const summaryEventTiles = summaryTile.getElementsByClassName('mx_EventTile');
|
||||
const summaryEventTiles = summaryTile.getElementsByClassName("mx_EventTile");
|
||||
// every event except for the room creation, room encryption, and Bob's
|
||||
// invite event should be in the event summary
|
||||
expect(summaryEventTiles.length).toEqual(tiles.length - 3);
|
||||
});
|
||||
|
||||
it('should not collapse beacons as part of creation events', function() {
|
||||
it("should not collapse beacons as part of creation events", function () {
|
||||
const events = mkCreationEvents();
|
||||
const creationEvent = events.find(event => event.getType() === 'm.room.create');
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(
|
||||
creationEvent.getSender(),
|
||||
creationEvent.getRoomId(),
|
||||
{ isLive: true },
|
||||
);
|
||||
const creationEvent = events.find((event) => event.getType() === "m.room.create");
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(creationEvent.getSender(), creationEvent.getRoomId(), {
|
||||
isLive: true,
|
||||
});
|
||||
const combinedEvents = [...events, beaconInfoEvent];
|
||||
TestUtilsMatrix.upsertRoomStateEvents(room, combinedEvents);
|
||||
const { container } = render(getComponent({ events: combinedEvents }));
|
||||
|
||||
const [summaryTile] = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
const [summaryTile] = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
|
||||
// beacon body is not in the summary
|
||||
expect(summaryTile.getElementsByClassName('mx_MBeaconBody').length).toBe(0);
|
||||
expect(summaryTile.getElementsByClassName("mx_MBeaconBody").length).toBe(0);
|
||||
// beacon tile is rendered
|
||||
expect(container.getElementsByClassName('mx_MBeaconBody').length).toBe(1);
|
||||
expect(container.getElementsByClassName("mx_MBeaconBody").length).toBe(1);
|
||||
});
|
||||
|
||||
it('should hide read-marker at the end of creation event summary', function() {
|
||||
it("should hide read-marker at the end of creation event summary", function () {
|
||||
const events = mkCreationEvents();
|
||||
TestUtilsMatrix.upsertRoomStateEvents(room, events);
|
||||
|
||||
const { container } = render(getComponent({
|
||||
events,
|
||||
readMarkerEventId: events[5].getId(),
|
||||
readMarkerVisible: true,
|
||||
}));
|
||||
const { container } = render(
|
||||
getComponent({
|
||||
events,
|
||||
readMarkerEventId: events[5].getId(),
|
||||
readMarkerVisible: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// find the <li> which wraps the read marker
|
||||
const [rm] = container.getElementsByClassName('mx_RoomView_myReadMarker_container');
|
||||
const [rm] = container.getElementsByClassName("mx_RoomView_myReadMarker_container");
|
||||
|
||||
const [messageList] = container.getElementsByClassName('mx_RoomView_MessageList');
|
||||
const [messageList] = container.getElementsByClassName("mx_RoomView_MessageList");
|
||||
const rows = messageList.children;
|
||||
expect(rows.length).toEqual(7); // 6 events + the NewRoomIntro
|
||||
expect(rm.previousSibling).toEqual(rows[5]);
|
||||
@@ -494,22 +530,22 @@ describe('MessagePanel', function() {
|
||||
expect(isReadMarkerVisible(rm)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should render Date separators for the events', function() {
|
||||
it("should render Date separators for the events", function () {
|
||||
const events = mkOneDayEvents();
|
||||
const { queryAllByRole } = render(getComponent({ events }));
|
||||
const dates = queryAllByRole('separator');
|
||||
const dates = queryAllByRole("separator");
|
||||
|
||||
expect(dates.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('appends events into summaries during forward pagination without changing key', () => {
|
||||
it("appends events into summaries during forward pagination without changing key", () => {
|
||||
const events = mkMelsEvents().slice(1, 11);
|
||||
|
||||
const { container, rerender } = render(getComponent({ events }));
|
||||
let els = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
let els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute('data-testid')).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(',').length).toEqual(10);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(10);
|
||||
|
||||
const updatedEvents = [
|
||||
...events,
|
||||
@@ -519,27 +555,27 @@ describe('MessagePanel', function() {
|
||||
user: "@user:id",
|
||||
target: bobMember,
|
||||
ts: Date.now(),
|
||||
mship: 'join',
|
||||
prevMship: 'join',
|
||||
name: 'A user',
|
||||
mship: "join",
|
||||
prevMship: "join",
|
||||
name: "A user",
|
||||
}),
|
||||
];
|
||||
rerender(getComponent({ events: updatedEvents }));
|
||||
|
||||
els = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute('data-testid')).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(',').length).toEqual(11);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(11);
|
||||
});
|
||||
|
||||
it('prepends events into summaries during backward pagination without changing key', () => {
|
||||
it("prepends events into summaries during backward pagination without changing key", () => {
|
||||
const events = mkMelsEvents().slice(1, 11);
|
||||
|
||||
const { container, rerender } = render(getComponent({ events }));
|
||||
let els = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
let els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute('data-testid')).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(',').length).toEqual(10);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(10);
|
||||
|
||||
const updatedEvents = [
|
||||
TestUtilsMatrix.mkMembership({
|
||||
@@ -548,28 +584,28 @@ describe('MessagePanel', function() {
|
||||
user: "@user:id",
|
||||
target: bobMember,
|
||||
ts: Date.now(),
|
||||
mship: 'join',
|
||||
prevMship: 'join',
|
||||
name: 'A user',
|
||||
mship: "join",
|
||||
prevMship: "join",
|
||||
name: "A user",
|
||||
}),
|
||||
...events,
|
||||
];
|
||||
rerender(getComponent({ events: updatedEvents }));
|
||||
|
||||
els = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute('data-testid')).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(',').length).toEqual(11);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(11);
|
||||
});
|
||||
|
||||
it('assigns different keys to summaries that get split up', () => {
|
||||
it("assigns different keys to summaries that get split up", () => {
|
||||
const events = mkMelsEvents().slice(1, 11);
|
||||
|
||||
const { container, rerender } = render(getComponent({ events }));
|
||||
let els = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
let els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute('data-testid')).toEqual(`eventlistsummary-${events[0].getId()}`);
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(',').length).toEqual(10);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual(`eventlistsummary-${events[0].getId()}`);
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(10);
|
||||
|
||||
const updatedEvents = [
|
||||
...events.slice(0, 5),
|
||||
@@ -584,13 +620,13 @@ describe('MessagePanel', function() {
|
||||
rerender(getComponent({ events: updatedEvents }));
|
||||
|
||||
// summaries split becuase room messages are not summarised
|
||||
els = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(2);
|
||||
expect(els[0].getAttribute('data-testid')).toEqual(`eventlistsummary-${events[0].getId()}`);
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(',').length).toEqual(5);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual(`eventlistsummary-${events[0].getId()}`);
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(5);
|
||||
|
||||
expect(els[1].getAttribute('data-testid')).toEqual(`eventlistsummary-${events[5].getId()}`);
|
||||
expect(els[1].getAttribute("data-scroll-tokens").split(',').length).toEqual(5);
|
||||
expect(els[1].getAttribute("data-testid")).toEqual(`eventlistsummary-${events[5].getId()}`);
|
||||
expect(els[1].getAttribute("data-scroll-tokens").split(",").length).toEqual(5);
|
||||
});
|
||||
|
||||
// We test this because setting lookups can be *slow*, and we don't want
|
||||
@@ -638,9 +674,9 @@ describe('MessagePanel', function() {
|
||||
];
|
||||
const { container } = render(getComponent({ events }, { showHiddenEvents: true }));
|
||||
|
||||
const els = container.getElementsByClassName('mx_GenericEventListSummary');
|
||||
const els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(',').length).toEqual(3);
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ describe("RightPanel", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const roomChanged = new Promise<void>(resolve => {
|
||||
const ref = dis.register(payload => {
|
||||
const roomChanged = new Promise<void>((resolve) => {
|
||||
const ref = dis.register((payload) => {
|
||||
if (payload.action === Action.ActiveRoomChanged) {
|
||||
dis.unregister(ref);
|
||||
resolve();
|
||||
@@ -84,12 +84,11 @@ describe("RightPanel", () => {
|
||||
await RightPanelStore.instance.onReady();
|
||||
};
|
||||
|
||||
const waitForRpsUpdate = () =>
|
||||
new Promise<void>(resolve => RightPanelStore.instance.once(UPDATE_EVENT, resolve));
|
||||
const waitForRpsUpdate = () => new Promise<void>((resolve) => RightPanelStore.instance.once(UPDATE_EVENT, resolve));
|
||||
|
||||
it("navigates from room summary to member list", async () => {
|
||||
const r1 = mkRoom(cli, "r1");
|
||||
cli.getRoom.mockImplementation(roomId => roomId === "r1" ? r1 : null);
|
||||
cli.getRoom.mockImplementation((roomId) => (roomId === "r1" ? r1 : null));
|
||||
|
||||
// Set up right panel state
|
||||
const realGetValue = SettingsStore.getValue;
|
||||
@@ -127,7 +126,7 @@ describe("RightPanel", () => {
|
||||
const r1 = mkRoom(cli, "r1");
|
||||
const r2 = mkRoom(cli, "r2");
|
||||
|
||||
cli.getRoom.mockImplementation(roomId => {
|
||||
cli.getRoom.mockImplementation((roomId) => {
|
||||
if (roomId === "r1") return r1;
|
||||
if (roomId === "r2") return r2;
|
||||
return null;
|
||||
@@ -168,7 +167,7 @@ describe("RightPanel", () => {
|
||||
// We want to verify that as we change to room 2, we should always have
|
||||
// the correct right panel state for whichever room we are showing.
|
||||
const instance = wrapper.find(_RightPanel).instance() as _RightPanel;
|
||||
const rendered = new Promise<void>(resolve => {
|
||||
const rendered = new Promise<void>((resolve) => {
|
||||
jest.spyOn(instance, "render").mockImplementation(() => {
|
||||
const { props, state } = instance;
|
||||
if (props.room.roomId === "r2" && state.phase === RightPanelPhases.RoomMemberList) {
|
||||
|
||||
@@ -63,7 +63,7 @@ describe("<RoomSearchView/>", () => {
|
||||
it("should show a spinner before the promise resolves", async () => {
|
||||
const deferred = defer<ISearchResults>();
|
||||
|
||||
render((
|
||||
render(
|
||||
<RoomSearchView
|
||||
term="search term"
|
||||
scope={SearchScope.All}
|
||||
@@ -72,50 +72,57 @@ describe("<RoomSearchView/>", () => {
|
||||
permalinkCreator={permalinkCreator}
|
||||
className="someClass"
|
||||
onUpdate={jest.fn()}
|
||||
/>
|
||||
));
|
||||
/>,
|
||||
);
|
||||
|
||||
await screen.findByTestId("messagePanelSearchSpinner");
|
||||
});
|
||||
|
||||
it("should render results when the promise resolves", async () => {
|
||||
render((
|
||||
render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomSearchView
|
||||
term="search term"
|
||||
scope={SearchScope.All}
|
||||
promise={Promise.resolve<ISearchResults>({
|
||||
results: [
|
||||
SearchResult.fromJson({
|
||||
rank: 1,
|
||||
result: {
|
||||
room_id: room.roomId,
|
||||
event_id: "$2",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 1,
|
||||
content: { body: "Foo Test Bar", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
},
|
||||
context: {
|
||||
profile_info: {},
|
||||
events_before: [{
|
||||
SearchResult.fromJson(
|
||||
{
|
||||
rank: 1,
|
||||
result: {
|
||||
room_id: room.roomId,
|
||||
event_id: "$1",
|
||||
event_id: "$2",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 1,
|
||||
content: { body: "Before", msgtype: "m.text" },
|
||||
content: { body: "Foo Test Bar", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
}],
|
||||
events_after: [{
|
||||
room_id: room.roomId,
|
||||
event_id: "$3",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 1,
|
||||
content: { body: "After", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
}],
|
||||
},
|
||||
context: {
|
||||
profile_info: {},
|
||||
events_before: [
|
||||
{
|
||||
room_id: room.roomId,
|
||||
event_id: "$1",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 1,
|
||||
content: { body: "Before", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
},
|
||||
],
|
||||
events_after: [
|
||||
{
|
||||
room_id: room.roomId,
|
||||
event_id: "$3",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 1,
|
||||
content: { body: "After", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}, eventMapper),
|
||||
eventMapper,
|
||||
),
|
||||
],
|
||||
highlights: [],
|
||||
count: 1,
|
||||
@@ -125,8 +132,8 @@ describe("<RoomSearchView/>", () => {
|
||||
className="someClass"
|
||||
onUpdate={jest.fn()}
|
||||
/>
|
||||
</MatrixClientContext.Provider>
|
||||
));
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
await screen.findByText("Before");
|
||||
await screen.findByText("Foo Test Bar");
|
||||
@@ -134,29 +141,32 @@ describe("<RoomSearchView/>", () => {
|
||||
});
|
||||
|
||||
it("should highlight words correctly", async () => {
|
||||
render((
|
||||
render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomSearchView
|
||||
term="search term"
|
||||
scope={SearchScope.Room}
|
||||
promise={Promise.resolve<ISearchResults>({
|
||||
results: [
|
||||
SearchResult.fromJson({
|
||||
rank: 1,
|
||||
result: {
|
||||
room_id: room.roomId,
|
||||
event_id: "$2",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 1,
|
||||
content: { body: "Foo Test Bar", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
SearchResult.fromJson(
|
||||
{
|
||||
rank: 1,
|
||||
result: {
|
||||
room_id: room.roomId,
|
||||
event_id: "$2",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 1,
|
||||
content: { body: "Foo Test Bar", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
},
|
||||
context: {
|
||||
profile_info: {},
|
||||
events_before: [],
|
||||
events_after: [],
|
||||
},
|
||||
},
|
||||
context: {
|
||||
profile_info: {},
|
||||
events_before: [],
|
||||
events_after: [],
|
||||
},
|
||||
}, eventMapper),
|
||||
eventMapper,
|
||||
),
|
||||
],
|
||||
highlights: ["test"],
|
||||
count: 1,
|
||||
@@ -166,8 +176,8 @@ describe("<RoomSearchView/>", () => {
|
||||
className="someClass"
|
||||
onUpdate={jest.fn()}
|
||||
/>
|
||||
</MatrixClientContext.Provider>
|
||||
));
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
const text = await screen.findByText("Test");
|
||||
expect(text).toHaveClass("mx_EventTile_searchHighlight");
|
||||
@@ -176,22 +186,25 @@ describe("<RoomSearchView/>", () => {
|
||||
it("should show spinner above results when backpaginating", async () => {
|
||||
const searchResults: ISearchResults = {
|
||||
results: [
|
||||
SearchResult.fromJson({
|
||||
rank: 1,
|
||||
result: {
|
||||
room_id: room.roomId,
|
||||
event_id: "$2",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 1,
|
||||
content: { body: "Foo Test Bar", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
SearchResult.fromJson(
|
||||
{
|
||||
rank: 1,
|
||||
result: {
|
||||
room_id: room.roomId,
|
||||
event_id: "$2",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 1,
|
||||
content: { body: "Foo Test Bar", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
},
|
||||
context: {
|
||||
profile_info: {},
|
||||
events_before: [],
|
||||
events_after: [],
|
||||
},
|
||||
},
|
||||
context: {
|
||||
profile_info: {},
|
||||
events_before: [],
|
||||
events_after: [],
|
||||
},
|
||||
}, eventMapper),
|
||||
eventMapper,
|
||||
),
|
||||
],
|
||||
highlights: ["test"],
|
||||
next_batch: "next_batch",
|
||||
@@ -202,27 +215,30 @@ describe("<RoomSearchView/>", () => {
|
||||
...searchResults,
|
||||
results: [
|
||||
...searchResults.results,
|
||||
SearchResult.fromJson({
|
||||
rank: 1,
|
||||
result: {
|
||||
room_id: room.roomId,
|
||||
event_id: "$4",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 4,
|
||||
content: { body: "Potato", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
SearchResult.fromJson(
|
||||
{
|
||||
rank: 1,
|
||||
result: {
|
||||
room_id: room.roomId,
|
||||
event_id: "$4",
|
||||
sender: client.getUserId(),
|
||||
origin_server_ts: 4,
|
||||
content: { body: "Potato", msgtype: "m.text" },
|
||||
type: EventType.RoomMessage,
|
||||
},
|
||||
context: {
|
||||
profile_info: {},
|
||||
events_before: [],
|
||||
events_after: [],
|
||||
},
|
||||
},
|
||||
context: {
|
||||
profile_info: {},
|
||||
events_before: [],
|
||||
events_after: [],
|
||||
},
|
||||
}, eventMapper),
|
||||
eventMapper,
|
||||
),
|
||||
],
|
||||
next_batch: undefined,
|
||||
});
|
||||
|
||||
render((
|
||||
render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomSearchView
|
||||
term="search term"
|
||||
@@ -233,8 +249,8 @@ describe("<RoomSearchView/>", () => {
|
||||
className="someClass"
|
||||
onUpdate={jest.fn()}
|
||||
/>
|
||||
</MatrixClientContext.Provider>
|
||||
));
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
await screen.findByRole("progressbar");
|
||||
await screen.findByText("Potato");
|
||||
@@ -244,7 +260,7 @@ describe("<RoomSearchView/>", () => {
|
||||
it("should handle resolutions after unmounting sanely", async () => {
|
||||
const deferred = defer<ISearchResults>();
|
||||
|
||||
const { unmount } = render((
|
||||
const { unmount } = render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomSearchView
|
||||
term="search term"
|
||||
@@ -255,8 +271,8 @@ describe("<RoomSearchView/>", () => {
|
||||
className="someClass"
|
||||
onUpdate={jest.fn()}
|
||||
/>
|
||||
</MatrixClientContext.Provider>
|
||||
));
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
unmount();
|
||||
deferred.resolve({
|
||||
@@ -268,7 +284,7 @@ describe("<RoomSearchView/>", () => {
|
||||
it("should handle rejections after unmounting sanely", async () => {
|
||||
const deferred = defer<ISearchResults>();
|
||||
|
||||
const { unmount } = render((
|
||||
const { unmount } = render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomSearchView
|
||||
term="search term"
|
||||
@@ -279,8 +295,8 @@ describe("<RoomSearchView/>", () => {
|
||||
className="someClass"
|
||||
onUpdate={jest.fn()}
|
||||
/>
|
||||
</MatrixClientContext.Provider>
|
||||
));
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
unmount();
|
||||
deferred.reject({
|
||||
@@ -292,7 +308,7 @@ describe("<RoomSearchView/>", () => {
|
||||
it("should show modal if error is encountered", async () => {
|
||||
const deferred = defer<ISearchResults>();
|
||||
|
||||
render((
|
||||
render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomSearchView
|
||||
term="search term"
|
||||
@@ -303,8 +319,8 @@ describe("<RoomSearchView/>", () => {
|
||||
className="someClass"
|
||||
onUpdate={jest.fn()}
|
||||
/>
|
||||
</MatrixClientContext.Provider>
|
||||
));
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
deferred.reject(new Error("Some error"));
|
||||
|
||||
await screen.findByText("Search failed");
|
||||
|
||||
@@ -85,7 +85,7 @@ describe("RoomStatusBar", () => {
|
||||
expect(pendingEvents[2].threadRootId).toBe(rootEvent.getId());
|
||||
|
||||
// Filters out the non thread events
|
||||
expect(pendingEvents.every(ev => ev.getId() !== event.getId())).toBe(true);
|
||||
expect(pendingEvents.every((ev) => ev.getId() !== event.getId())).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ describe("RoomStatusBarUnsentMessages", () => {
|
||||
const title = "test title";
|
||||
const description = "test description";
|
||||
const buttonsText = "test buttons";
|
||||
const buttons = <div>{ buttonsText }</div>;
|
||||
const buttons = <div>{buttonsText}</div>;
|
||||
|
||||
beforeEach(() => {
|
||||
render(
|
||||
|
||||
@@ -75,7 +75,7 @@ describe("RoomView", () => {
|
||||
stores.client = cli;
|
||||
stores.rightPanelStore.useUnitTestClient(cli);
|
||||
|
||||
jest.spyOn(VoipUserMapper.sharedInstance(), 'getVirtualRoomForRoom').mockResolvedValue(null);
|
||||
jest.spyOn(VoipUserMapper.sharedInstance(), "getVirtualRoomForRoom").mockResolvedValue(null);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -85,7 +85,7 @@ describe("RoomView", () => {
|
||||
|
||||
const mountRoomView = async (): Promise<ReactWrapper> => {
|
||||
if (stores.roomViewStore.getRoomId() !== room.roomId) {
|
||||
const switchedRoom = new Promise<void>(resolve => {
|
||||
const switchedRoom = new Promise<void>((resolve) => {
|
||||
const subFn = () => {
|
||||
if (stores.roomViewStore.getRoomId()) {
|
||||
stores.roomViewStore.off(UPDATE_EVENT, subFn);
|
||||
@@ -121,7 +121,7 @@ describe("RoomView", () => {
|
||||
|
||||
const renderRoomView = async (): Promise<ReturnType<typeof render>> => {
|
||||
if (stores.roomViewStore.getRoomId() !== room.roomId) {
|
||||
const switchedRoom = new Promise<void>(resolve => {
|
||||
const switchedRoom = new Promise<void>((resolve) => {
|
||||
const subFn = () => {
|
||||
if (stores.roomViewStore.getRoomId()) {
|
||||
stores.roomViewStore.off(UPDATE_EVENT, subFn);
|
||||
@@ -180,13 +180,15 @@ describe("RoomView", () => {
|
||||
cli.isRoomEncrypted.mockReturnValue(true);
|
||||
|
||||
// and fake an encryption event into the room to prompt it to re-check
|
||||
room.addLiveEvents([new MatrixEvent({
|
||||
type: "m.room.encryption",
|
||||
sender: cli.getUserId()!,
|
||||
content: {},
|
||||
event_id: "someid",
|
||||
room_id: room.roomId,
|
||||
})]);
|
||||
room.addLiveEvents([
|
||||
new MatrixEvent({
|
||||
type: "m.room.encryption",
|
||||
sender: cli.getUserId()!,
|
||||
content: {},
|
||||
event_id: "someid",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
]);
|
||||
|
||||
// URL previews should now be disabled
|
||||
expect(roomViewInstance.state.showUrlPreview).toBe(false);
|
||||
@@ -200,13 +202,13 @@ describe("RoomView", () => {
|
||||
expect(roomViewInstance.state.liveTimeline).not.toEqual(oldTimeline);
|
||||
});
|
||||
|
||||
describe('with virtual rooms', () => {
|
||||
describe("with virtual rooms", () => {
|
||||
it("checks for a virtual room on initial load", async () => {
|
||||
const { container } = await renderRoomView();
|
||||
expect(VoipUserMapper.sharedInstance().getVirtualRoomForRoom).toHaveBeenCalledWith(room.roomId);
|
||||
|
||||
// quick check that rendered without error
|
||||
expect(container.querySelector('.mx_ErrorBoundary')).toBeFalsy();
|
||||
expect(container.querySelector(".mx_ErrorBoundary")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("checks for a virtual room on room event", async () => {
|
||||
@@ -307,7 +309,7 @@ describe("RoomView", () => {
|
||||
it("clicking retry should set the room state to new dispatch a local room event", async () => {
|
||||
jest.spyOn(defaultDispatcher, "dispatch");
|
||||
const { getByText } = await renderRoomView();
|
||||
fireEvent.click(getByText('Retry'));
|
||||
fireEvent.click(getByText("Retry"));
|
||||
expect(localRoom.state).toBe(LocalRoomState.NEW);
|
||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: "local_room_event",
|
||||
|
||||
@@ -53,17 +53,17 @@ describe("SpaceHierarchy", () => {
|
||||
it("shows room", () => {
|
||||
showRoom(client, hierarchy, "room-id2");
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
"action": Action.ViewRoom,
|
||||
"should_peek": true,
|
||||
"room_alias": "canonical-alias",
|
||||
"room_id": "room-id2",
|
||||
"via_servers": [],
|
||||
"oob_data": {
|
||||
action: Action.ViewRoom,
|
||||
should_peek: true,
|
||||
room_alias: "canonical-alias",
|
||||
room_id: "room-id2",
|
||||
via_servers: [],
|
||||
oob_data: {
|
||||
avatarUrl: undefined,
|
||||
name: "canonical-alias",
|
||||
},
|
||||
"roomType": undefined,
|
||||
"metricsTrigger": "RoomDirectory",
|
||||
roomType: undefined,
|
||||
metricsTrigger: "RoomDirectory",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,31 +14,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import TabbedView, { Tab, TabLocation } from "../../../src/components/structures/TabbedView";
|
||||
|
||||
describe('<TabbedView />', () => {
|
||||
const generalTab = new Tab(
|
||||
'GENERAL',
|
||||
'General',
|
||||
'general',
|
||||
<div>general</div>,
|
||||
);
|
||||
const labsTab = new Tab(
|
||||
'LABS',
|
||||
'Labs',
|
||||
'labs',
|
||||
<div>labs</div>,
|
||||
);
|
||||
const securityTab = new Tab(
|
||||
'SECURITY',
|
||||
'Security',
|
||||
'security',
|
||||
<div>security</div>,
|
||||
);
|
||||
describe("<TabbedView />", () => {
|
||||
const generalTab = new Tab("GENERAL", "General", "general", <div>general</div>);
|
||||
const labsTab = new Tab("LABS", "Labs", "labs", <div>labs</div>);
|
||||
const securityTab = new Tab("SECURITY", "Security", "security", <div>security</div>);
|
||||
const defaultProps = {
|
||||
tabLocation: TabLocation.LEFT,
|
||||
tabs: [generalTab, labsTab, securityTab],
|
||||
@@ -47,39 +32,39 @@ describe('<TabbedView />', () => {
|
||||
|
||||
const getTabTestId = (tab: Tab): string => `settings-tab-${tab.id}`;
|
||||
const getActiveTab = (container: HTMLElement): Element | undefined =>
|
||||
container.getElementsByClassName('mx_TabbedView_tabLabel_active')[0];
|
||||
container.getElementsByClassName("mx_TabbedView_tabLabel_active")[0];
|
||||
const getActiveTabBody = (container: HTMLElement): Element | undefined =>
|
||||
container.getElementsByClassName('mx_TabbedView_tabPanel')[0];
|
||||
container.getElementsByClassName("mx_TabbedView_tabPanel")[0];
|
||||
|
||||
it('renders tabs', () => {
|
||||
it("renders tabs", () => {
|
||||
const { container } = render(getComponent());
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders first tab as active tab when no initialTabId', () => {
|
||||
it("renders first tab as active tab when no initialTabId", () => {
|
||||
const { container } = render(getComponent());
|
||||
expect(getActiveTab(container).textContent).toEqual(generalTab.label);
|
||||
expect(getActiveTabBody(container).textContent).toEqual('general');
|
||||
expect(getActiveTabBody(container).textContent).toEqual("general");
|
||||
});
|
||||
|
||||
it('renders first tab as active tab when initialTabId is not valid', () => {
|
||||
const { container } = render(getComponent({ initialTabId: 'bad-tab-id' }));
|
||||
it("renders first tab as active tab when initialTabId is not valid", () => {
|
||||
const { container } = render(getComponent({ initialTabId: "bad-tab-id" }));
|
||||
expect(getActiveTab(container).textContent).toEqual(generalTab.label);
|
||||
expect(getActiveTabBody(container).textContent).toEqual('general');
|
||||
expect(getActiveTabBody(container).textContent).toEqual("general");
|
||||
});
|
||||
|
||||
it('renders initialTabId tab as active when valid', () => {
|
||||
it("renders initialTabId tab as active when valid", () => {
|
||||
const { container } = render(getComponent({ initialTabId: securityTab.id }));
|
||||
expect(getActiveTab(container).textContent).toEqual(securityTab.label);
|
||||
expect(getActiveTabBody(container).textContent).toEqual('security');
|
||||
expect(getActiveTabBody(container).textContent).toEqual("security");
|
||||
});
|
||||
|
||||
it('renders without error when there are no tabs', () => {
|
||||
it("renders without error when there are no tabs", () => {
|
||||
const { container } = render(getComponent({ tabs: [] }));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('sets active tab on tab click', () => {
|
||||
it("sets active tab on tab click", () => {
|
||||
const { container, getByTestId } = render(getComponent());
|
||||
|
||||
act(() => {
|
||||
@@ -87,10 +72,10 @@ describe('<TabbedView />', () => {
|
||||
});
|
||||
|
||||
expect(getActiveTab(container).textContent).toEqual(securityTab.label);
|
||||
expect(getActiveTabBody(container).textContent).toEqual('security');
|
||||
expect(getActiveTabBody(container).textContent).toEqual("security");
|
||||
});
|
||||
|
||||
it('calls onchange on on tab click', () => {
|
||||
it("calls onchange on on tab click", () => {
|
||||
const onChange = jest.fn();
|
||||
const { getByTestId } = render(getComponent({ onChange }));
|
||||
|
||||
@@ -101,7 +86,7 @@ describe('<TabbedView />', () => {
|
||||
expect(onChange).toHaveBeenCalledWith(securityTab.id);
|
||||
});
|
||||
|
||||
it('keeps same tab active when order of tabs changes', () => {
|
||||
it("keeps same tab active when order of tabs changes", () => {
|
||||
// start with middle tab active
|
||||
const { container, rerender } = render(getComponent({ initialTabId: labsTab.id }));
|
||||
|
||||
@@ -113,7 +98,7 @@ describe('<TabbedView />', () => {
|
||||
expect(getActiveTab(container).textContent).toEqual(labsTab.label);
|
||||
});
|
||||
|
||||
it('does not reactivate inititalTabId on rerender', () => {
|
||||
it("does not reactivate inititalTabId on rerender", () => {
|
||||
const { container, getByTestId, rerender } = render(getComponent());
|
||||
|
||||
expect(getActiveTab(container).textContent).toEqual(generalTab.label);
|
||||
|
||||
@@ -14,22 +14,22 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { mocked } from "jest-mock";
|
||||
import 'focus-visible'; // to fix context menus
|
||||
import "focus-visible"; // to fix context menus
|
||||
|
||||
import ThreadPanel, { ThreadFilterType, ThreadPanelHeader } from '../../../src/components/structures/ThreadPanel';
|
||||
import { _t } from '../../../src/languageHandler';
|
||||
import ResizeNotifier from '../../../src/utils/ResizeNotifier';
|
||||
import { RoomPermalinkCreator } from '../../../src/utils/permalinks/Permalinks';
|
||||
import { createTestClient, mkStubRoom } from '../../test-utils';
|
||||
import ThreadPanel, { ThreadFilterType, ThreadPanelHeader } from "../../../src/components/structures/ThreadPanel";
|
||||
import { _t } from "../../../src/languageHandler";
|
||||
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
|
||||
import { RoomPermalinkCreator } from "../../../src/utils/permalinks/Permalinks";
|
||||
import { createTestClient, mkStubRoom } from "../../test-utils";
|
||||
import { shouldShowFeedback } from "../../../src/utils/Feedback";
|
||||
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
||||
|
||||
jest.mock("../../../src/utils/Feedback");
|
||||
|
||||
describe('ThreadPanel', () => {
|
||||
describe("ThreadPanel", () => {
|
||||
describe("Feedback prompt", () => {
|
||||
const cli = createTestClient();
|
||||
const room = mkStubRoom("!room:server", "room", cli);
|
||||
@@ -38,59 +38,66 @@ describe('ThreadPanel', () => {
|
||||
it("should show feedback prompt if feedback is enabled", () => {
|
||||
mocked(shouldShowFeedback).mockReturnValue(true);
|
||||
|
||||
render(<MatrixClientContext.Provider value={cli}>
|
||||
<ThreadPanel
|
||||
roomId="!room:server"
|
||||
onClose={jest.fn()}
|
||||
resizeNotifier={new ResizeNotifier()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>);
|
||||
render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<ThreadPanel
|
||||
roomId="!room:server"
|
||||
onClose={jest.fn()}
|
||||
resizeNotifier={new ResizeNotifier()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
expect(screen.queryByText("Give feedback")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should hide feedback prompt if feedback is disabled", () => {
|
||||
mocked(shouldShowFeedback).mockReturnValue(false);
|
||||
|
||||
render(<MatrixClientContext.Provider value={cli}>
|
||||
<ThreadPanel
|
||||
roomId="!room:server"
|
||||
onClose={jest.fn()}
|
||||
resizeNotifier={new ResizeNotifier()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>);
|
||||
render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<ThreadPanel
|
||||
roomId="!room:server"
|
||||
onClose={jest.fn()}
|
||||
resizeNotifier={new ResizeNotifier()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
expect(screen.queryByText("Give feedback")).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Header', () => {
|
||||
it('expect that All filter for ThreadPanelHeader properly renders Show: All threads', () => {
|
||||
describe("Header", () => {
|
||||
it("expect that All filter for ThreadPanelHeader properly renders Show: All threads", () => {
|
||||
const { asFragment } = render(
|
||||
<ThreadPanelHeader
|
||||
empty={false}
|
||||
filterOption={ThreadFilterType.All}
|
||||
setFilterOption={() => undefined} />,
|
||||
setFilterOption={() => undefined}
|
||||
/>,
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('expect that My filter for ThreadPanelHeader properly renders Show: My threads', () => {
|
||||
it("expect that My filter for ThreadPanelHeader properly renders Show: My threads", () => {
|
||||
const { asFragment } = render(
|
||||
<ThreadPanelHeader
|
||||
empty={false}
|
||||
filterOption={ThreadFilterType.My}
|
||||
setFilterOption={() => undefined} />,
|
||||
setFilterOption={() => undefined}
|
||||
/>,
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('expect that ThreadPanelHeader properly opens a context menu when clicked on the button', () => {
|
||||
it("expect that ThreadPanelHeader properly opens a context menu when clicked on the button", () => {
|
||||
const { container } = render(
|
||||
<ThreadPanelHeader
|
||||
empty={false}
|
||||
filterOption={ThreadFilterType.All}
|
||||
setFilterOption={() => undefined} />,
|
||||
setFilterOption={() => undefined}
|
||||
/>,
|
||||
);
|
||||
const found = container.querySelector(".mx_ThreadPanel_dropdown");
|
||||
expect(found).toBeTruthy();
|
||||
@@ -99,18 +106,19 @@ describe('ThreadPanel', () => {
|
||||
expect(screen.queryByRole("menu")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('expect that ThreadPanelHeader has the correct option selected in the context menu', () => {
|
||||
it("expect that ThreadPanelHeader has the correct option selected in the context menu", () => {
|
||||
const { container } = render(
|
||||
<ThreadPanelHeader
|
||||
empty={false}
|
||||
filterOption={ThreadFilterType.All}
|
||||
setFilterOption={() => undefined} />,
|
||||
setFilterOption={() => undefined}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(container.querySelector(".mx_ThreadPanel_dropdown"));
|
||||
const found = screen.queryAllByRole("menuitemradio");
|
||||
expect(found).toHaveLength(2);
|
||||
const foundButton = screen.queryByRole("menuitemradio", { checked: true });
|
||||
expect(foundButton.textContent).toEqual(`${_t("All threads")}${_t('Shows all threads from current room')}`);
|
||||
expect(foundButton.textContent).toEqual(`${_t("All threads")}${_t("Shows all threads from current room")}`);
|
||||
expect(foundButton).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,27 +51,25 @@ describe("ThreadView", () => {
|
||||
const [event, setEvent] = useState(rootEvent);
|
||||
changeEvent = setEvent;
|
||||
|
||||
return <MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={getRoomContext(room, {
|
||||
canSendMessages: true,
|
||||
})}>
|
||||
<ThreadView
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
mxEvent={event}
|
||||
resizeNotifier={new ResizeNotifier()}
|
||||
/>
|
||||
</RoomContext.Provider>,
|
||||
</MatrixClientContext.Provider>;
|
||||
return (
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider
|
||||
value={getRoomContext(room, {
|
||||
canSendMessages: true,
|
||||
})}
|
||||
>
|
||||
<ThreadView room={room} onClose={jest.fn()} mxEvent={event} resizeNotifier={new ResizeNotifier()} />
|
||||
</RoomContext.Provider>
|
||||
,
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
async function getComponent(): Promise<RenderResult> {
|
||||
const renderResult = render(
|
||||
<TestThreadView />,
|
||||
);
|
||||
const renderResult = render(<TestThreadView />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(() => getByTestId(renderResult.container, 'spinner')).toThrow();
|
||||
expect(() => getByTestId(renderResult.container, "spinner")).toThrow();
|
||||
});
|
||||
|
||||
return renderResult;
|
||||
@@ -92,9 +90,12 @@ describe("ThreadView", () => {
|
||||
"event_id": rootEvent.getId(),
|
||||
"is_falling_back": true,
|
||||
"m.in_reply_to": {
|
||||
"event_id": rootEvent.getThread().lastReply((ev: MatrixEvent) => {
|
||||
return ev.isRelation(THREAD_RELATION_TYPE.name);
|
||||
}).getId(),
|
||||
event_id: rootEvent
|
||||
.getThread()
|
||||
.lastReply((ev: MatrixEvent) => {
|
||||
return ev.isRelation(THREAD_RELATION_TYPE.name);
|
||||
})
|
||||
.getId(),
|
||||
},
|
||||
"rel_type": RelationType.Thread,
|
||||
},
|
||||
@@ -133,7 +134,9 @@ describe("ThreadView", () => {
|
||||
await sendMessage(container, "Hello world!");
|
||||
|
||||
expect(mockClient.sendMessage).toHaveBeenCalledWith(
|
||||
ROOM_ID, rootEvent.getId(), expectedMessageBody(rootEvent, "Hello world!"),
|
||||
ROOM_ID,
|
||||
rootEvent.getId(),
|
||||
expectedMessageBody(rootEvent, "Hello world!"),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -154,7 +157,9 @@ describe("ThreadView", () => {
|
||||
await sendMessage(container, "yolo");
|
||||
|
||||
expect(mockClient.sendMessage).toHaveBeenCalledWith(
|
||||
ROOM_ID, rootEvent2.getId(), expectedMessageBody(rootEvent2, "yolo"),
|
||||
ROOM_ID,
|
||||
rootEvent2.getId(),
|
||||
expectedMessageBody(rootEvent2, "yolo"),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
import { render, RenderResult } from "@testing-library/react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount, ReactWrapper } from "enzyme";
|
||||
import { MessageEvent } from 'matrix-events-sdk';
|
||||
import { MessageEvent } from "matrix-events-sdk";
|
||||
import { ReceiptType } from "matrix-js-sdk/src/@types/read_receipts";
|
||||
import {
|
||||
EventTimelineSet,
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
Room,
|
||||
RoomEvent,
|
||||
TimelineWindow,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
|
||||
import {
|
||||
FeatureSupport,
|
||||
@@ -37,13 +37,13 @@ import {
|
||||
ThreadEvent,
|
||||
ThreadFilterType,
|
||||
} from "matrix-js-sdk/src/models/thread";
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import TimelinePanel from '../../../src/components/structures/TimelinePanel';
|
||||
import TimelinePanel from "../../../src/components/structures/TimelinePanel";
|
||||
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
||||
import { MatrixClientPeg } from '../../../src/MatrixClientPeg';
|
||||
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
||||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
import { isCallEvent } from '../../../src/components/structures/LegacyCallEventGrouper';
|
||||
import { isCallEvent } from "../../../src/components/structures/LegacyCallEventGrouper";
|
||||
import { flushPromises, mkRoom, stubClient } from "../../test-utils";
|
||||
|
||||
const newReceipt = (eventId: string, userId: string, readTs: number, fullyReadTs: number): MatrixEvent => {
|
||||
@@ -81,13 +81,15 @@ const renderPanel = (room: Room, events: MatrixEvent[]): RenderResult => {
|
||||
const mockEvents = (room: Room, count = 2): MatrixEvent[] => {
|
||||
const events: MatrixEvent[] = [];
|
||||
for (let index = 0; index < count; index++) {
|
||||
events.push(new MatrixEvent({
|
||||
room_id: room.roomId,
|
||||
event_id: `${room.roomId}_event_${index}`,
|
||||
type: EventType.RoomMessage,
|
||||
user_id: "userId",
|
||||
content: MessageEvent.from(`Event${index}`).serialize().content,
|
||||
}));
|
||||
events.push(
|
||||
new MatrixEvent({
|
||||
room_id: room.roomId,
|
||||
event_id: `${room.roomId}_event_${index}`,
|
||||
type: EventType.RoomMessage,
|
||||
user_id: "userId",
|
||||
content: MessageEvent.from(`Event${index}`).serialize().content,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return events;
|
||||
@@ -100,13 +102,13 @@ const setupTestData = (): [MatrixClient, Room, MatrixEvent[]] => {
|
||||
return [client, room, events];
|
||||
};
|
||||
|
||||
describe('TimelinePanel', () => {
|
||||
describe("TimelinePanel", () => {
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
});
|
||||
|
||||
describe('read receipts and markers', () => {
|
||||
it('should forget the read marker when asked to', () => {
|
||||
describe("read receipts and markers", () => {
|
||||
it("should forget the read marker when asked to", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const readMarkersSent: string[] = [];
|
||||
|
||||
@@ -131,22 +133,19 @@ describe('TimelinePanel', () => {
|
||||
|
||||
const roomId = "#room:example.com";
|
||||
const userId = cli.credentials.userId!;
|
||||
const room = new Room(
|
||||
roomId,
|
||||
cli,
|
||||
userId,
|
||||
{ pendingEventOrdering: PendingEventOrdering.Detached },
|
||||
);
|
||||
const room = new Room(roomId, cli, userId, { pendingEventOrdering: PendingEventOrdering.Detached });
|
||||
|
||||
// Create a TimelinePanel with ev0 already present
|
||||
const timelineSet = new EventTimelineSet(room, {});
|
||||
timelineSet.addLiveEvent(ev0);
|
||||
const component: ReactWrapper<TimelinePanel> = mount(<TimelinePanel
|
||||
timelineSet={timelineSet}
|
||||
manageReadMarkers={true}
|
||||
manageReadReceipts={true}
|
||||
eventId={ev0.getId()}
|
||||
/>);
|
||||
const component: ReactWrapper<TimelinePanel> = mount(
|
||||
<TimelinePanel
|
||||
timelineSet={timelineSet}
|
||||
manageReadMarkers={true}
|
||||
manageReadReceipts={true}
|
||||
eventId={ev0.getId()}
|
||||
/>,
|
||||
);
|
||||
const timelinePanel = component.instance() as TimelinePanel;
|
||||
|
||||
// An event arrived, and we read it
|
||||
@@ -208,8 +207,8 @@ describe('TimelinePanel', () => {
|
||||
expect(props.onEventScrolledIntoView).toHaveBeenCalledWith(events[1].getId());
|
||||
});
|
||||
|
||||
describe('onRoomTimeline', () => {
|
||||
it('ignores events for other timelines', () => {
|
||||
describe("onRoomTimeline", () => {
|
||||
it("ignores events for other timelines", () => {
|
||||
const [client, room, events] = setupTestData();
|
||||
|
||||
const otherTimelineSet = { room: room as Room } as EventTimelineSet;
|
||||
@@ -220,7 +219,7 @@ describe('TimelinePanel', () => {
|
||||
onEventScrolledIntoView: jest.fn(),
|
||||
};
|
||||
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
|
||||
|
||||
render(<TimelinePanel {...props} />);
|
||||
|
||||
@@ -231,12 +230,12 @@ describe('TimelinePanel', () => {
|
||||
expect(paginateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ignores timeline updates without a live event', () => {
|
||||
it("ignores timeline updates without a live event", () => {
|
||||
const [client, room, events] = setupTestData();
|
||||
|
||||
const props = getProps(room, events);
|
||||
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
|
||||
|
||||
render(<TimelinePanel {...props} />);
|
||||
|
||||
@@ -247,12 +246,12 @@ describe('TimelinePanel', () => {
|
||||
expect(paginateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ignores timeline where toStartOfTimeline is true', () => {
|
||||
it("ignores timeline where toStartOfTimeline is true", () => {
|
||||
const [client, room, events] = setupTestData();
|
||||
|
||||
const props = getProps(room, events);
|
||||
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
|
||||
|
||||
render(<TimelinePanel {...props} />);
|
||||
|
||||
@@ -264,12 +263,12 @@ describe('TimelinePanel', () => {
|
||||
expect(paginateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('advances the timeline window', () => {
|
||||
it("advances the timeline window", () => {
|
||||
const [client, room, events] = setupTestData();
|
||||
|
||||
const props = getProps(room, events);
|
||||
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
|
||||
|
||||
render(<TimelinePanel {...props} />);
|
||||
|
||||
@@ -280,7 +279,7 @@ describe('TimelinePanel', () => {
|
||||
expect(paginateSpy).toHaveBeenCalledWith(EventTimeline.FORWARDS, 1, false);
|
||||
});
|
||||
|
||||
it('advances the overlay timeline window', async () => {
|
||||
it("advances the overlay timeline window", async () => {
|
||||
const [client, room, events] = setupTestData();
|
||||
|
||||
const virtualRoom = mkRoom(client, "virtualRoomId");
|
||||
@@ -292,7 +291,7 @@ describe('TimelinePanel', () => {
|
||||
overlayTimelineSet,
|
||||
};
|
||||
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
|
||||
const paginateSpy = jest.spyOn(TimelineWindow.prototype, "paginate").mockClear();
|
||||
|
||||
render(<TimelinePanel {...props} />);
|
||||
|
||||
@@ -306,25 +305,21 @@ describe('TimelinePanel', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('with overlayTimeline', () => {
|
||||
it('renders merged timeline', () => {
|
||||
describe("with overlayTimeline", () => {
|
||||
it("renders merged timeline", () => {
|
||||
const [client, room, events] = setupTestData();
|
||||
const virtualRoom = mkRoom(client, "virtualRoomId");
|
||||
const virtualCallInvite = new MatrixEvent({
|
||||
type: 'm.call.invite',
|
||||
type: "m.call.invite",
|
||||
room_id: virtualRoom.roomId,
|
||||
event_id: `virtualCallEvent1`,
|
||||
});
|
||||
const virtualCallMetaEvent = new MatrixEvent({
|
||||
type: 'org.matrix.call.sdp_stream_metadata_changed',
|
||||
type: "org.matrix.call.sdp_stream_metadata_changed",
|
||||
room_id: virtualRoom.roomId,
|
||||
event_id: `virtualCallEvent2`,
|
||||
});
|
||||
const virtualEvents = [
|
||||
virtualCallInvite,
|
||||
...mockEvents(virtualRoom),
|
||||
virtualCallMetaEvent,
|
||||
];
|
||||
const virtualEvents = [virtualCallInvite, ...mockEvents(virtualRoom), virtualCallMetaEvent];
|
||||
const { timelineSet: overlayTimelineSet } = getProps(virtualRoom, virtualEvents);
|
||||
|
||||
const props = {
|
||||
@@ -335,8 +330,8 @@ describe('TimelinePanel', () => {
|
||||
|
||||
const { container } = render(<TimelinePanel {...props} />);
|
||||
|
||||
const eventTiles = container.querySelectorAll('.mx_EventTile');
|
||||
const eventTileIds = [...eventTiles].map(tileElement => tileElement.getAttribute('data-event-id'));
|
||||
const eventTiles = container.querySelectorAll(".mx_EventTile");
|
||||
const eventTileIds = [...eventTiles].map((tileElement) => tileElement.getAttribute("data-event-id"));
|
||||
expect(eventTileIds).toEqual([
|
||||
// main timeline events are included
|
||||
events[1].getId(),
|
||||
@@ -368,16 +363,22 @@ describe('TimelinePanel', () => {
|
||||
});
|
||||
|
||||
room = new Room("roomId", client, "userId");
|
||||
allThreads = new EventTimelineSet(room, {
|
||||
pendingEvents: false,
|
||||
}, undefined, undefined, ThreadFilterType.All);
|
||||
allThreads = new EventTimelineSet(
|
||||
room,
|
||||
{
|
||||
pendingEvents: false,
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
ThreadFilterType.All,
|
||||
);
|
||||
const timeline = new EventTimeline(allThreads);
|
||||
allThreads.getLiveTimeline = () => timeline;
|
||||
allThreads.getTimelineForEvent = () => timeline;
|
||||
|
||||
reply1 = new MatrixEvent({
|
||||
room_id: room.roomId,
|
||||
event_id: 'event_reply_1',
|
||||
event_id: "event_reply_1",
|
||||
type: EventType.RoomMessage,
|
||||
user_id: "userId",
|
||||
content: MessageEvent.from(`ReplyEvent1`).serialize().content,
|
||||
@@ -385,7 +386,7 @@ describe('TimelinePanel', () => {
|
||||
|
||||
reply2 = new MatrixEvent({
|
||||
room_id: room.roomId,
|
||||
event_id: 'event_reply_2',
|
||||
event_id: "event_reply_2",
|
||||
type: EventType.RoomMessage,
|
||||
user_id: "userId",
|
||||
content: MessageEvent.from(`ReplyEvent2`).serialize().content,
|
||||
@@ -393,7 +394,7 @@ describe('TimelinePanel', () => {
|
||||
|
||||
root = new MatrixEvent({
|
||||
room_id: room.roomId,
|
||||
event_id: 'event_root_1',
|
||||
event_id: "event_root_1",
|
||||
type: EventType.RoomMessage,
|
||||
user_id: "userId",
|
||||
content: MessageEvent.from(`RootEvent`).serialize().content,
|
||||
@@ -410,13 +411,13 @@ describe('TimelinePanel', () => {
|
||||
roomId === room.roomId ? eventMap[eventId]?.event : {};
|
||||
});
|
||||
|
||||
it('updates thread previews', async () => {
|
||||
it("updates thread previews", async () => {
|
||||
root.setUnsigned({
|
||||
"m.relations": {
|
||||
[THREAD_RELATION_TYPE.name]: {
|
||||
"latest_event": reply1.event,
|
||||
"count": 1,
|
||||
"current_user_participated": true,
|
||||
latest_event: reply1.event,
|
||||
count: 1,
|
||||
current_user_participated: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -432,11 +433,7 @@ describe('TimelinePanel', () => {
|
||||
|
||||
const dom = render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<TimelinePanel
|
||||
timelineSet={allThreads}
|
||||
manageReadReceipts
|
||||
sendReadReceiptOnLoad
|
||||
/>
|
||||
<TimelinePanel timelineSet={allThreads} manageReadReceipts sendReadReceiptOnLoad />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
await dom.findByText("RootEvent");
|
||||
@@ -446,9 +443,9 @@ describe('TimelinePanel', () => {
|
||||
root.setUnsigned({
|
||||
"m.relations": {
|
||||
[THREAD_RELATION_TYPE.name]: {
|
||||
"latest_event": reply2.event,
|
||||
"count": 2,
|
||||
"current_user_participated": true,
|
||||
latest_event: reply2.event,
|
||||
count: 2,
|
||||
current_user_participated: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -460,13 +457,13 @@ describe('TimelinePanel', () => {
|
||||
expect(replyToEvent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ignores thread updates for unknown threads', async () => {
|
||||
it("ignores thread updates for unknown threads", async () => {
|
||||
root.setUnsigned({
|
||||
"m.relations": {
|
||||
[THREAD_RELATION_TYPE.name]: {
|
||||
"latest_event": reply1.event,
|
||||
"count": 1,
|
||||
"current_user_participated": true,
|
||||
latest_event: reply1.event,
|
||||
count: 1,
|
||||
current_user_participated: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -499,11 +496,7 @@ describe('TimelinePanel', () => {
|
||||
|
||||
const dom = render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<TimelinePanel
|
||||
timelineSet={allThreads}
|
||||
manageReadReceipts
|
||||
sendReadReceiptOnLoad
|
||||
/>
|
||||
<TimelinePanel timelineSet={allThreads} manageReadReceipts sendReadReceiptOnLoad />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
await dom.findByText("RootEvent");
|
||||
|
||||
@@ -42,10 +42,7 @@ describe("<UserMenu>", () => {
|
||||
client.getUserId() || "",
|
||||
client.getDeviceId() || "",
|
||||
);
|
||||
voiceBroadcastRecording = new VoiceBroadcastRecording(
|
||||
voiceBroadcastInfoEvent,
|
||||
client,
|
||||
);
|
||||
voiceBroadcastRecording = new VoiceBroadcastRecording(voiceBroadcastInfoEvent, client);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -100,11 +100,9 @@ describe("<ForgotPassword>", () => {
|
||||
|
||||
describe("when starting a password reset flow", () => {
|
||||
beforeEach(() => {
|
||||
renderResult = render(<ForgotPassword
|
||||
serverConfig={serverConfig}
|
||||
onComplete={onComplete}
|
||||
onLoginClick={onLoginClick}
|
||||
/>);
|
||||
renderResult = render(
|
||||
<ForgotPassword serverConfig={serverConfig} onComplete={onComplete} onLoginClick={onLoginClick} />,
|
||||
);
|
||||
});
|
||||
|
||||
it("should show the email input and mention the homeserver", () => {
|
||||
@@ -115,11 +113,9 @@ describe("<ForgotPassword>", () => {
|
||||
describe("and updating the server config", () => {
|
||||
beforeEach(() => {
|
||||
serverConfig.hsName = "example2.com";
|
||||
renderResult.rerender(<ForgotPassword
|
||||
serverConfig={serverConfig}
|
||||
onComplete={onComplete}
|
||||
onLoginClick={onLoginClick}
|
||||
/>);
|
||||
renderResult.rerender(
|
||||
<ForgotPassword serverConfig={serverConfig} onComplete={onComplete} onLoginClick={onLoginClick} />,
|
||||
);
|
||||
});
|
||||
|
||||
it("should show the new homeserver server name", () => {
|
||||
@@ -171,10 +167,12 @@ describe("<ForgotPassword>", () => {
|
||||
});
|
||||
|
||||
it("should show an info about that", () => {
|
||||
expect(screen.getByText(
|
||||
"Cannot reach homeserver: "
|
||||
+ "Ensure you have a stable internet connection, or get in touch with the server admin",
|
||||
)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
"Cannot reach homeserver: " +
|
||||
"Ensure you have a stable internet connection, or get in touch with the server admin",
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen, waitForElementToBeRemoved } from "@testing-library/react";
|
||||
import { mocked, MockedObject } from 'jest-mock';
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import SdkConfig from '../../../../src/SdkConfig';
|
||||
import SdkConfig from "../../../../src/SdkConfig";
|
||||
import { mkServerConfig, mockPlatformPeg, unmockPlatformPeg } from "../../../test-utils";
|
||||
import Login from "../../../../src/components/structures/auth/Login";
|
||||
import BasePlatform from "../../../../src/BasePlatform";
|
||||
@@ -29,7 +29,7 @@ jest.mock("matrix-js-sdk/src/matrix");
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
describe('Login', function() {
|
||||
describe("Login", function () {
|
||||
let platform: MockedObject<BasePlatform>;
|
||||
|
||||
const mockClient = mocked({
|
||||
@@ -37,14 +37,14 @@ describe('Login', function() {
|
||||
loginFlows: jest.fn(),
|
||||
} as unknown as MatrixClient);
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
SdkConfig.put({
|
||||
brand: "test-brand",
|
||||
disable_custom_urls: true,
|
||||
});
|
||||
mockClient.login.mockClear().mockResolvedValue({});
|
||||
mockClient.loginFlows.mockClear().mockResolvedValue({ flows: [{ type: "m.login.password" }] });
|
||||
mocked(createClient).mockImplementation(opts => {
|
||||
mocked(createClient).mockImplementation((opts) => {
|
||||
mockClient.idBaseUrl = opts.idBaseUrl;
|
||||
mockClient.baseUrl = opts.baseUrl;
|
||||
return mockClient;
|
||||
@@ -58,26 +58,28 @@ describe('Login', function() {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
afterEach(function () {
|
||||
fetchMock.restore();
|
||||
SdkConfig.unset(); // we touch the config, so clean up
|
||||
unmockPlatformPeg();
|
||||
});
|
||||
|
||||
function getRawComponent(hsUrl = "https://matrix.org", isUrl = "https://vector.im") {
|
||||
return <Login
|
||||
serverConfig={mkServerConfig(hsUrl, isUrl)}
|
||||
onLoggedIn={() => { }}
|
||||
onRegisterClick={() => { }}
|
||||
onServerConfigChange={() => { }}
|
||||
/>;
|
||||
return (
|
||||
<Login
|
||||
serverConfig={mkServerConfig(hsUrl, isUrl)}
|
||||
onLoggedIn={() => {}}
|
||||
onRegisterClick={() => {}}
|
||||
onServerConfigChange={() => {}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function getComponent(hsUrl?: string, isUrl?: string) {
|
||||
return render(getRawComponent(hsUrl, isUrl));
|
||||
}
|
||||
|
||||
it('should show form with change server link', async () => {
|
||||
it("should show form with change server link", async () => {
|
||||
SdkConfig.put({
|
||||
brand: "test-brand",
|
||||
disable_custom_urls: false,
|
||||
@@ -90,7 +92,7 @@ describe('Login', function() {
|
||||
expect(container.querySelector(".mx_ServerPicker_change")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show form without change server link when custom URLs disabled', async () => {
|
||||
it("should show form without change server link when custom URLs disabled", async () => {
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
@@ -122,19 +124,25 @@ describe('Login', function() {
|
||||
|
||||
it("should show multiple SSO buttons if multiple identity_providers are available", async () => {
|
||||
mockClient.loginFlows.mockResolvedValue({
|
||||
flows: [{
|
||||
"type": "m.login.sso",
|
||||
"identity_providers": [{
|
||||
id: "a",
|
||||
name: "Provider 1",
|
||||
}, {
|
||||
id: "b",
|
||||
name: "Provider 2",
|
||||
}, {
|
||||
id: "c",
|
||||
name: "Provider 3",
|
||||
}],
|
||||
}],
|
||||
flows: [
|
||||
{
|
||||
type: "m.login.sso",
|
||||
identity_providers: [
|
||||
{
|
||||
id: "a",
|
||||
name: "Provider 1",
|
||||
},
|
||||
{
|
||||
id: "b",
|
||||
name: "Provider 2",
|
||||
},
|
||||
{
|
||||
id: "c",
|
||||
name: "Provider 3",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { container } = getComponent();
|
||||
@@ -146,9 +154,11 @@ describe('Login', function() {
|
||||
|
||||
it("should show single SSO button if identity_providers is null", async () => {
|
||||
mockClient.loginFlows.mockResolvedValue({
|
||||
flows: [{
|
||||
"type": "m.login.sso",
|
||||
}],
|
||||
flows: [
|
||||
{
|
||||
type: "m.login.sso",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { container } = getComponent();
|
||||
@@ -160,9 +170,11 @@ describe('Login', function() {
|
||||
|
||||
it("should handle serverConfig updates correctly", async () => {
|
||||
mockClient.loginFlows.mockResolvedValue({
|
||||
flows: [{
|
||||
"type": "m.login.sso",
|
||||
}],
|
||||
flows: [
|
||||
{
|
||||
type: "m.login.sso",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { container, rerender } = render(getRawComponent());
|
||||
|
||||
@@ -15,37 +15,42 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen, waitForElementToBeRemoved } from "@testing-library/react";
|
||||
import { createClient, MatrixClient } from 'matrix-js-sdk/src/matrix';
|
||||
import { MatrixError } from 'matrix-js-sdk/src/http-api/errors';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixError } from "matrix-js-sdk/src/http-api/errors";
|
||||
import { mocked } from "jest-mock";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import SdkConfig, { DEFAULTS } from '../../../../src/SdkConfig';
|
||||
import SdkConfig, { DEFAULTS } from "../../../../src/SdkConfig";
|
||||
import { mkServerConfig, mockPlatformPeg, unmockPlatformPeg } from "../../../test-utils";
|
||||
import Registration from "../../../../src/components/structures/auth/Registration";
|
||||
|
||||
jest.mock('matrix-js-sdk/src/matrix');
|
||||
jest.mock("matrix-js-sdk/src/matrix");
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe('Registration', function() {
|
||||
describe("Registration", function () {
|
||||
const registerRequest = jest.fn();
|
||||
const mockClient = mocked({
|
||||
registerRequest,
|
||||
loginFlows: jest.fn(),
|
||||
} as unknown as MatrixClient);
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
SdkConfig.put({
|
||||
...DEFAULTS,
|
||||
disable_custom_urls: true,
|
||||
});
|
||||
mockClient.registerRequest.mockRejectedValueOnce(new MatrixError({
|
||||
flows: [{ stages: [] }],
|
||||
}, 401));
|
||||
mockClient.registerRequest.mockRejectedValueOnce(
|
||||
new MatrixError(
|
||||
{
|
||||
flows: [{ stages: [] }],
|
||||
},
|
||||
401,
|
||||
),
|
||||
);
|
||||
mockClient.loginFlows.mockClear().mockResolvedValue({ flows: [{ type: "m.login.password" }] });
|
||||
mocked(createClient).mockImplementation(opts => {
|
||||
mocked(createClient).mockImplementation((opts) => {
|
||||
mockClient.idBaseUrl = opts.idBaseUrl;
|
||||
mockClient.baseUrl = opts.baseUrl;
|
||||
return mockClient;
|
||||
@@ -59,14 +64,14 @@ describe('Registration', function() {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
afterEach(function () {
|
||||
fetchMock.restore();
|
||||
SdkConfig.unset(); // we touch the config, so clean up
|
||||
unmockPlatformPeg();
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
defaultDeviceDisplayName: 'test-device-display-name',
|
||||
defaultDeviceDisplayName: "test-device-display-name",
|
||||
makeRegistrationUrl: jest.fn(),
|
||||
onLoggedIn: jest.fn(),
|
||||
onLoginClick: jest.fn(),
|
||||
@@ -74,22 +79,19 @@ describe('Registration', function() {
|
||||
};
|
||||
|
||||
function getRawComponent(hsUrl = "https://matrix.org", isUrl = "https://vector.im") {
|
||||
return <Registration
|
||||
{...defaultProps}
|
||||
serverConfig={mkServerConfig(hsUrl, isUrl)}
|
||||
/>;
|
||||
return <Registration {...defaultProps} serverConfig={mkServerConfig(hsUrl, isUrl)} />;
|
||||
}
|
||||
|
||||
function getComponent(hsUrl?: string, isUrl?: string) {
|
||||
return render(getRawComponent(hsUrl, isUrl));
|
||||
}
|
||||
|
||||
it('should show server picker', async function() {
|
||||
it("should show server picker", async function () {
|
||||
const { container } = getComponent();
|
||||
expect(container.querySelector(".mx_ServerPicker")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show form when custom URLs disabled', async function() {
|
||||
it("should show form when custom URLs disabled", async function () {
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
expect(container.querySelector("form")).toBeTruthy();
|
||||
@@ -106,9 +108,11 @@ describe('Registration', function() {
|
||||
|
||||
it("should handle serverConfig updates correctly", async () => {
|
||||
mockClient.loginFlows.mockResolvedValue({
|
||||
flows: [{
|
||||
"type": "m.login.sso",
|
||||
}],
|
||||
flows: [
|
||||
{
|
||||
type: "m.login.sso",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { container, rerender } = render(getRawComponent());
|
||||
|
||||
@@ -21,11 +21,15 @@ describe("Validation", () => {
|
||||
const handler = withValidation({
|
||||
rules: [],
|
||||
});
|
||||
return expect(handler({
|
||||
value: "value",
|
||||
focused: true,
|
||||
})).resolves.toEqual(expect.objectContaining({
|
||||
valid: true,
|
||||
}));
|
||||
return expect(
|
||||
handler({
|
||||
value: "value",
|
||||
focused: true,
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
valid: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,28 +14,28 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { logger } from 'matrix-js-sdk/src/logger';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mount } from "enzyme";
|
||||
import { mocked } from "jest-mock";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import RecordingPlayback, { PlaybackLayout } from '../../../../src/components/views/audio_messages/RecordingPlayback';
|
||||
import { Playback } from '../../../../src/audio/Playback';
|
||||
import RoomContext, { TimelineRenderingType } from '../../../../src/contexts/RoomContext';
|
||||
import { createAudioContext } from '../../../../src/audio/compat';
|
||||
import { findByTestId, flushPromises } from '../../../test-utils';
|
||||
import PlaybackWaveform from '../../../../src/components/views/audio_messages/PlaybackWaveform';
|
||||
import RecordingPlayback, { PlaybackLayout } from "../../../../src/components/views/audio_messages/RecordingPlayback";
|
||||
import { Playback } from "../../../../src/audio/Playback";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext";
|
||||
import { createAudioContext } from "../../../../src/audio/compat";
|
||||
import { findByTestId, flushPromises } from "../../../test-utils";
|
||||
import PlaybackWaveform from "../../../../src/components/views/audio_messages/PlaybackWaveform";
|
||||
import SeekBar from "../../../../src/components/views/audio_messages/SeekBar";
|
||||
import PlaybackClock from "../../../../src/components/views/audio_messages/PlaybackClock";
|
||||
|
||||
jest.mock('../../../../src/audio/compat', () => ({
|
||||
jest.mock("../../../../src/audio/compat", () => ({
|
||||
createAudioContext: jest.fn(),
|
||||
decodeOgg: jest.fn().mockResolvedValue({}),
|
||||
}));
|
||||
|
||||
describe('<RecordingPlayback />', () => {
|
||||
describe("<RecordingPlayback />", () => {
|
||||
const mockAudioBufferSourceNode = {
|
||||
addEventListener: jest.fn(),
|
||||
connect: jest.fn(),
|
||||
@@ -57,7 +57,7 @@ describe('<RecordingPlayback />', () => {
|
||||
|
||||
const mockChannelData = new Float32Array();
|
||||
|
||||
const defaultRoom = { roomId: '!room:server.org', timelineRenderingType: TimelineRenderingType.File };
|
||||
const defaultRoom = { roomId: "!room:server.org", timelineRenderingType: TimelineRenderingType.File };
|
||||
const getComponent = (props: React.ComponentProps<typeof RecordingPlayback>, room = defaultRoom) =>
|
||||
mount(<RecordingPlayback {...props} />, {
|
||||
wrappingComponent: RoomContext.Provider,
|
||||
@@ -65,29 +65,27 @@ describe('<RecordingPlayback />', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(logger, 'error').mockRestore();
|
||||
jest.spyOn(logger, "error").mockRestore();
|
||||
mockAudioBuffer.getChannelData.mockClear().mockReturnValue(mockChannelData);
|
||||
mockAudioContext.decodeAudioData.mockReset().mockImplementation(
|
||||
(_b, callback) => callback(mockAudioBuffer),
|
||||
);
|
||||
mockAudioContext.decodeAudioData.mockReset().mockImplementation((_b, callback) => callback(mockAudioBuffer));
|
||||
mocked(createAudioContext).mockReturnValue(mockAudioContext as unknown as AudioContext);
|
||||
});
|
||||
|
||||
const getPlayButton = component => findByTestId(component, 'play-pause-button').at(0);
|
||||
const getPlayButton = (component) => findByTestId(component, "play-pause-button").at(0);
|
||||
|
||||
it('renders recording playback', () => {
|
||||
it("renders recording playback", () => {
|
||||
const playback = new Playback(new ArrayBuffer(8));
|
||||
const component = getComponent({ playback });
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('disables play button while playback is decoding', async () => {
|
||||
it("disables play button while playback is decoding", async () => {
|
||||
const playback = new Playback(new ArrayBuffer(8));
|
||||
const component = getComponent({ playback });
|
||||
expect(getPlayButton(component).props().disabled).toBeTruthy();
|
||||
});
|
||||
|
||||
it('enables play button when playback is finished decoding', async () => {
|
||||
it("enables play button when playback is finished decoding", async () => {
|
||||
const playback = new Playback(new ArrayBuffer(8));
|
||||
const component = getComponent({ playback });
|
||||
await flushPromises();
|
||||
@@ -95,43 +93,41 @@ describe('<RecordingPlayback />', () => {
|
||||
expect(getPlayButton(component).props().disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('displays error when playback decoding fails', async () => {
|
||||
it("displays error when playback decoding fails", async () => {
|
||||
// stub logger to keep console clean from expected error
|
||||
jest.spyOn(logger, 'error').mockReturnValue(undefined);
|
||||
jest.spyOn(logger, 'warn').mockReturnValue(undefined);
|
||||
mockAudioContext.decodeAudioData.mockImplementation(
|
||||
(_b, _cb, error) => error(new Error('oh no')),
|
||||
);
|
||||
jest.spyOn(logger, "error").mockReturnValue(undefined);
|
||||
jest.spyOn(logger, "warn").mockReturnValue(undefined);
|
||||
mockAudioContext.decodeAudioData.mockImplementation((_b, _cb, error) => error(new Error("oh no")));
|
||||
const playback = new Playback(new ArrayBuffer(8));
|
||||
const component = getComponent({ playback });
|
||||
await flushPromises();
|
||||
expect(component.find('.text-warning').length).toBeFalsy();
|
||||
expect(component.find(".text-warning").length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('displays pre-prepared playback with correct playback phase', async () => {
|
||||
it("displays pre-prepared playback with correct playback phase", async () => {
|
||||
const playback = new Playback(new ArrayBuffer(8));
|
||||
await playback.prepare();
|
||||
const component = getComponent({ playback });
|
||||
// playback already decoded, button is not disabled
|
||||
expect(getPlayButton(component).props().disabled).toBeFalsy();
|
||||
expect(component.find('.text-warning').length).toBeFalsy();
|
||||
expect(component.find(".text-warning").length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('toggles playback on play pause button click', async () => {
|
||||
it("toggles playback on play pause button click", async () => {
|
||||
const playback = new Playback(new ArrayBuffer(8));
|
||||
jest.spyOn(playback, 'toggle').mockResolvedValue(undefined);
|
||||
jest.spyOn(playback, "toggle").mockResolvedValue(undefined);
|
||||
await playback.prepare();
|
||||
const component = getComponent({ playback });
|
||||
|
||||
act(() => {
|
||||
getPlayButton(component).simulate('click');
|
||||
getPlayButton(component).simulate("click");
|
||||
});
|
||||
|
||||
expect(playback.toggle).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('Composer Layout', () => {
|
||||
it('should have a waveform, no seek bar, and clock', () => {
|
||||
describe("Composer Layout", () => {
|
||||
it("should have a waveform, no seek bar, and clock", () => {
|
||||
const playback = new Playback(new ArrayBuffer(8));
|
||||
const component = getComponent({ playback, layout: PlaybackLayout.Composer });
|
||||
|
||||
@@ -141,8 +137,8 @@ describe('<RecordingPlayback />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Timeline Layout', () => {
|
||||
it('should have a waveform, a seek bar, and clock', () => {
|
||||
describe("Timeline Layout", () => {
|
||||
it("should have a waveform, a seek bar, and clock", () => {
|
||||
const playback = new Playback(new ArrayBuffer(8));
|
||||
const component = getComponent({ playback, layout: PlaybackLayout.Timeline });
|
||||
|
||||
@@ -151,7 +147,7 @@ describe('<RecordingPlayback />', () => {
|
||||
expect(component.find(SeekBar).length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should be the default', () => {
|
||||
it("should be the default", () => {
|
||||
const playback = new Playback(new ArrayBuffer(8));
|
||||
const component = getComponent({ playback }); // no layout set for test
|
||||
|
||||
|
||||
@@ -30,9 +30,10 @@ describe("SeekBar", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
seekBarRef = createRef();
|
||||
jest.spyOn(window, "requestAnimationFrame").mockImplementation(
|
||||
(callback: FrameRequestCallback) => { frameRequestCallback = callback; return 0; },
|
||||
);
|
||||
jest.spyOn(window, "requestAnimationFrame").mockImplementation((callback: FrameRequestCallback) => {
|
||||
frameRequestCallback = callback;
|
||||
return 0;
|
||||
});
|
||||
playback = createTestPlayback();
|
||||
});
|
||||
|
||||
|
||||
@@ -36,14 +36,11 @@ describe("MemberAvatar", () => {
|
||||
let member: RoomMember;
|
||||
|
||||
function getComponent(props) {
|
||||
return <RoomContext.Provider value={getRoomContext(room, {})}>
|
||||
<MemberAvatar
|
||||
member={null}
|
||||
width={35}
|
||||
height={35}
|
||||
{...props}
|
||||
/>
|
||||
</RoomContext.Provider>;
|
||||
return (
|
||||
<RoomContext.Provider value={getRoomContext(room, {})}>
|
||||
<MemberAvatar member={null} width={35} height={35} {...props} />
|
||||
</RoomContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -14,32 +14,28 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
import {
|
||||
Beacon,
|
||||
RoomMember,
|
||||
MatrixEvent,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
import { LocationAssetType } from 'matrix-js-sdk/src/@types/location';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { Beacon, RoomMember, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { LocationAssetType } from "matrix-js-sdk/src/@types/location";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import BeaconListItem from '../../../../src/components/views/beacon/BeaconListItem';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import BeaconListItem from "../../../../src/components/views/beacon/BeaconListItem";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
makeBeaconEvent,
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithBeacons,
|
||||
} from '../../../test-utils';
|
||||
} from "../../../test-utils";
|
||||
|
||||
describe('<BeaconListItem />', () => {
|
||||
describe("<BeaconListItem />", () => {
|
||||
// 14.03.2022 16:15
|
||||
const now = 1647270879403;
|
||||
// go back in time to create beacons and locations in the past
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(now - 600000);
|
||||
const roomId = '!room:server';
|
||||
const aliceId = '@alice:server';
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now - 600000);
|
||||
const roomId = "!room:server";
|
||||
const aliceId = "@alice:server";
|
||||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getUserId: jest.fn().mockReturnValue(aliceId),
|
||||
@@ -47,36 +43,41 @@ describe('<BeaconListItem />', () => {
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
});
|
||||
|
||||
const aliceBeaconEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
const alicePinBeaconEvent = makeBeaconInfoEvent(aliceId,
|
||||
const aliceBeaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
const alicePinBeaconEvent = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true, assetType: LocationAssetType.Pin, description: "Alice's car" },
|
||||
'$alice-room1-1',
|
||||
"$alice-room1-1",
|
||||
);
|
||||
const pinBeaconWithoutDescription = makeBeaconInfoEvent(aliceId,
|
||||
const pinBeaconWithoutDescription = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true, assetType: LocationAssetType.Pin },
|
||||
'$alice-room1-1',
|
||||
"$alice-room1-1",
|
||||
);
|
||||
|
||||
const aliceLocation1 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: aliceBeaconEvent.getId(), geoUri: 'geo:51,41', timestamp: now - 1 },
|
||||
);
|
||||
const aliceLocation2 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: aliceBeaconEvent.getId(), geoUri: 'geo:52,42', timestamp: now - 500000 },
|
||||
);
|
||||
const aliceLocation1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: aliceBeaconEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
timestamp: now - 1,
|
||||
});
|
||||
const aliceLocation2 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: aliceBeaconEvent.getId(),
|
||||
geoUri: "geo:52,42",
|
||||
timestamp: now - 500000,
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
beacon: new Beacon(aliceBeaconEvent),
|
||||
};
|
||||
|
||||
const getComponent = (props = {}) => render(<MatrixClientContext.Provider value={mockClient}>
|
||||
<BeaconListItem {...defaultProps} {...props} />
|
||||
</MatrixClientContext.Provider>);
|
||||
const getComponent = (props = {}) =>
|
||||
render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<BeaconListItem {...defaultProps} {...props} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
const setupRoomWithBeacons = (beaconInfoEvents: MatrixEvent[], locationEvents?: MatrixEvent[]): Beacon[] => {
|
||||
const beacons = makeRoomWithBeacons(roomId, mockClient, beaconInfoEvents, locationEvents);
|
||||
@@ -84,102 +85,101 @@ describe('<BeaconListItem />', () => {
|
||||
const member = new RoomMember(roomId, aliceId);
|
||||
member.name = `Alice`;
|
||||
const room = mockClient.getRoom(roomId);
|
||||
jest.spyOn(room, 'getMember').mockReturnValue(member);
|
||||
jest.spyOn(room, "getMember").mockReturnValue(member);
|
||||
|
||||
return beacons;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.spyOn(Date, 'now').mockReturnValue(now);
|
||||
jest.spyOn(Date, "now").mockReturnValue(now);
|
||||
});
|
||||
|
||||
it('renders null when beacon is not live', () => {
|
||||
const notLiveBeacon = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: false },
|
||||
);
|
||||
it("renders null when beacon is not live", () => {
|
||||
const notLiveBeacon = makeBeaconInfoEvent(aliceId, roomId, { isLive: false });
|
||||
const [beacon] = setupRoomWithBeacons([notLiveBeacon]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
it('renders null when beacon has no location', () => {
|
||||
it("renders null when beacon has no location", () => {
|
||||
const [beacon] = setupRoomWithBeacons([aliceBeaconEvent]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('when a beacon is live and has locations', () => {
|
||||
it('renders beacon info', () => {
|
||||
describe("when a beacon is live and has locations", () => {
|
||||
it("renders beacon info", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const { asFragment } = getComponent({ beacon });
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('non-self beacons', () => {
|
||||
it('uses beacon description as beacon name', () => {
|
||||
describe("non-self beacons", () => {
|
||||
it("uses beacon description as beacon name", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector('.mx_BeaconStatus_label')).toHaveTextContent("Alice's car");
|
||||
expect(container.querySelector(".mx_BeaconStatus_label")).toHaveTextContent("Alice's car");
|
||||
});
|
||||
|
||||
it('uses beacon owner mxid as beacon name for a beacon without description', () => {
|
||||
it("uses beacon owner mxid as beacon name for a beacon without description", () => {
|
||||
const [beacon] = setupRoomWithBeacons([pinBeaconWithoutDescription], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector('.mx_BeaconStatus_label')).toHaveTextContent(aliceId);
|
||||
expect(container.querySelector(".mx_BeaconStatus_label")).toHaveTextContent(aliceId);
|
||||
});
|
||||
|
||||
it('renders location icon', () => {
|
||||
it("renders location icon", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector('.mx_StyledLiveBeaconIcon')).toBeTruthy();
|
||||
expect(container.querySelector(".mx_StyledLiveBeaconIcon")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('self locations', () => {
|
||||
it('renders beacon owner avatar', () => {
|
||||
describe("self locations", () => {
|
||||
it("renders beacon owner avatar", () => {
|
||||
const [beacon] = setupRoomWithBeacons([aliceBeaconEvent], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector('.mx_BaseAvatar')).toBeTruthy();
|
||||
expect(container.querySelector(".mx_BaseAvatar")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('uses beacon owner name as beacon name', () => {
|
||||
it("uses beacon owner name as beacon name", () => {
|
||||
const [beacon] = setupRoomWithBeacons([aliceBeaconEvent], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector('.mx_BeaconStatus_label')).toHaveTextContent("Alice");
|
||||
expect(container.querySelector(".mx_BeaconStatus_label")).toHaveTextContent("Alice");
|
||||
});
|
||||
});
|
||||
|
||||
describe('on location updates', () => {
|
||||
it('updates last updated time on location updated', () => {
|
||||
describe("on location updates", () => {
|
||||
it("updates last updated time on location updated", () => {
|
||||
const [beacon] = setupRoomWithBeacons([aliceBeaconEvent], [aliceLocation2]);
|
||||
const { container } = getComponent({ beacon });
|
||||
|
||||
expect(container.querySelector('.mx_BeaconListItem_lastUpdated'))
|
||||
.toHaveTextContent('Updated 9 minutes ago');
|
||||
expect(container.querySelector(".mx_BeaconListItem_lastUpdated")).toHaveTextContent(
|
||||
"Updated 9 minutes ago",
|
||||
);
|
||||
|
||||
// update to a newer location
|
||||
act(() => {
|
||||
beacon.addLocations([aliceLocation1]);
|
||||
});
|
||||
|
||||
expect(container.querySelector('.mx_BeaconListItem_lastUpdated'))
|
||||
.toHaveTextContent('Updated a few seconds ago');
|
||||
expect(container.querySelector(".mx_BeaconListItem_lastUpdated")).toHaveTextContent(
|
||||
"Updated a few seconds ago",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('interactions', () => {
|
||||
it('does not call onClick handler when clicking share button', () => {
|
||||
describe("interactions", () => {
|
||||
it("does not call onClick handler when clicking share button", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const onClick = jest.fn();
|
||||
const { getByTestId } = getComponent({ beacon, onClick });
|
||||
|
||||
fireEvent.click(getByTestId('open-location-in-osm'));
|
||||
fireEvent.click(getByTestId("open-location-in-osm"));
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls onClick handler when clicking outside of share buttons', () => {
|
||||
it("calls onClick handler when clicking outside of share buttons", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const onClick = jest.fn();
|
||||
const { container } = getComponent({ beacon, onClick });
|
||||
|
||||
@@ -14,36 +14,30 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import {
|
||||
Beacon,
|
||||
Room,
|
||||
RoomMember,
|
||||
MatrixEvent,
|
||||
getBeaconInfoIdentifier,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
import { mount } from "enzyme";
|
||||
import maplibregl from "maplibre-gl";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { Beacon, Room, RoomMember, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import BeaconMarker from '../../../../src/components/views/beacon/BeaconMarker';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import BeaconMarker from "../../../../src/components/views/beacon/BeaconMarker";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
makeBeaconEvent,
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithStateEvents,
|
||||
} from '../../../test-utils';
|
||||
import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils';
|
||||
} from "../../../test-utils";
|
||||
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
||||
|
||||
describe('<BeaconMarker />', () => {
|
||||
describe("<BeaconMarker />", () => {
|
||||
// 14.03.2022 16:15
|
||||
const now = 1647270879403;
|
||||
// stable date for snapshots
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(now);
|
||||
const roomId = '!room:server';
|
||||
const aliceId = '@alice:server';
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
const roomId = "!room:server";
|
||||
const aliceId = "@alice:server";
|
||||
|
||||
const aliceMember = new RoomMember(roomId, aliceId);
|
||||
|
||||
@@ -51,7 +45,7 @@ describe('<BeaconMarker />', () => {
|
||||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getClientWellKnown: jest.fn().mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
}),
|
||||
getUserId: jest.fn().mockReturnValue(aliceId),
|
||||
getRoom: jest.fn(),
|
||||
@@ -62,27 +56,23 @@ describe('<BeaconMarker />', () => {
|
||||
// as we update room state
|
||||
const setupRoom = (stateEvents: MatrixEvent[] = []): Room => {
|
||||
const room1 = makeRoomWithStateEvents(stateEvents, { roomId, mockClient });
|
||||
jest.spyOn(room1, 'getMember').mockReturnValue(aliceMember);
|
||||
jest.spyOn(room1, "getMember").mockReturnValue(aliceMember);
|
||||
return room1;
|
||||
};
|
||||
|
||||
const defaultEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
const notLiveEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: false },
|
||||
'$alice-room1-2',
|
||||
);
|
||||
const defaultEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
const notLiveEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: false }, "$alice-room1-2");
|
||||
|
||||
const location1 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: defaultEvent.getId(), geoUri: 'geo:51,41', timestamp: now + 1 },
|
||||
);
|
||||
const location2 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: defaultEvent.getId(), geoUri: 'geo:52,42', timestamp: now + 10000 },
|
||||
);
|
||||
const location1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: defaultEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
timestamp: now + 1,
|
||||
});
|
||||
const location2 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: defaultEvent.getId(),
|
||||
geoUri: "geo:52,42",
|
||||
timestamp: now + 10000,
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
map: mockMap,
|
||||
@@ -99,21 +89,21 @@ describe('<BeaconMarker />', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders nothing when beacon is not live', () => {
|
||||
it("renders nothing when beacon is not live", () => {
|
||||
const room = setupRoom([notLiveEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(notLiveEvent));
|
||||
const component = getComponent({ beacon });
|
||||
expect(component.html()).toBe(null);
|
||||
});
|
||||
|
||||
it('renders nothing when beacon has no location', () => {
|
||||
it("renders nothing when beacon has no location", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
const component = getComponent({ beacon });
|
||||
expect(component.html()).toBe(null);
|
||||
});
|
||||
|
||||
it('renders marker when beacon has location', () => {
|
||||
it("renders marker when beacon has location", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
@@ -121,12 +111,12 @@ describe('<BeaconMarker />', () => {
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('updates with new locations', () => {
|
||||
it("updates with new locations", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
const component = getComponent({ beacon });
|
||||
expect(component.find('SmartMarker').props()['geoUri']).toEqual('geo:51,41');
|
||||
expect(component.find("SmartMarker").props()["geoUri"]).toEqual("geo:51,41");
|
||||
|
||||
act(() => {
|
||||
beacon.addLocations([location2]);
|
||||
@@ -134,6 +124,6 @@ describe('<BeaconMarker />', () => {
|
||||
component.setProps({});
|
||||
|
||||
// updated to latest location
|
||||
expect(component.find('SmartMarker').props()['geoUri']).toEqual('geo:52,42');
|
||||
expect(component.find("SmartMarker").props()["geoUri"]).toEqual("geo:52,42");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,75 +14,75 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { Beacon } from 'matrix-js-sdk/src/matrix';
|
||||
import { mount } from "enzyme";
|
||||
import { Beacon } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import BeaconStatus from '../../../../src/components/views/beacon/BeaconStatus';
|
||||
import { BeaconDisplayStatus } from '../../../../src/components/views/beacon/displayStatus';
|
||||
import { findByTestId, makeBeaconInfoEvent } from '../../../test-utils';
|
||||
import BeaconStatus from "../../../../src/components/views/beacon/BeaconStatus";
|
||||
import { BeaconDisplayStatus } from "../../../../src/components/views/beacon/displayStatus";
|
||||
import { findByTestId, makeBeaconInfoEvent } from "../../../test-utils";
|
||||
|
||||
describe('<BeaconStatus />', () => {
|
||||
describe("<BeaconStatus />", () => {
|
||||
const defaultProps = {
|
||||
displayStatus: BeaconDisplayStatus.Loading,
|
||||
label: 'test label',
|
||||
label: "test label",
|
||||
withIcon: true,
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<BeaconStatus {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => mount(<BeaconStatus {...defaultProps} {...props} />);
|
||||
|
||||
it('renders loading state', () => {
|
||||
it("renders loading state", () => {
|
||||
const component = getComponent({ displayStatus: BeaconDisplayStatus.Loading });
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders stopped state', () => {
|
||||
it("renders stopped state", () => {
|
||||
const component = getComponent({ displayStatus: BeaconDisplayStatus.Stopped });
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders without icon', () => {
|
||||
it("renders without icon", () => {
|
||||
const component = getComponent({ withIcon: false, displayStatus: BeaconDisplayStatus.Stopped });
|
||||
expect(component.find('StyledLiveBeaconIcon').length).toBeFalsy();
|
||||
expect(component.find("StyledLiveBeaconIcon").length).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('active state', () => {
|
||||
it('renders without children', () => {
|
||||
describe("active state", () => {
|
||||
it("renders without children", () => {
|
||||
// mock for stable snapshot
|
||||
jest.spyOn(Date, 'now').mockReturnValue(123456789);
|
||||
const beacon = new Beacon(makeBeaconInfoEvent('@user:server', '!room:server', { isLive: false }, '$1'));
|
||||
jest.spyOn(Date, "now").mockReturnValue(123456789);
|
||||
const beacon = new Beacon(makeBeaconInfoEvent("@user:server", "!room:server", { isLive: false }, "$1"));
|
||||
const component = getComponent({ beacon, displayStatus: BeaconDisplayStatus.Active });
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders with children', () => {
|
||||
const beacon = new Beacon(makeBeaconInfoEvent('@user:server', '!room:sever', { isLive: false }));
|
||||
it("renders with children", () => {
|
||||
const beacon = new Beacon(makeBeaconInfoEvent("@user:server", "!room:sever", { isLive: false }));
|
||||
const component = getComponent({
|
||||
beacon,
|
||||
children: <span data-test-id='test'>test</span>,
|
||||
children: <span data-test-id="test">test</span>,
|
||||
displayStatus: BeaconDisplayStatus.Active,
|
||||
});
|
||||
expect(findByTestId(component, 'test-child')).toMatchSnapshot();
|
||||
expect(findByTestId(component, "test-child")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders static remaining time when displayLiveTimeRemaining is falsy', () => {
|
||||
it("renders static remaining time when displayLiveTimeRemaining is falsy", () => {
|
||||
// mock for stable snapshot
|
||||
jest.spyOn(Date, 'now').mockReturnValue(123456789);
|
||||
const beacon = new Beacon(makeBeaconInfoEvent('@user:server', '!room:server', { isLive: false }, '$1'));
|
||||
jest.spyOn(Date, "now").mockReturnValue(123456789);
|
||||
const beacon = new Beacon(makeBeaconInfoEvent("@user:server", "!room:server", { isLive: false }, "$1"));
|
||||
const component = getComponent({ beacon, displayStatus: BeaconDisplayStatus.Active });
|
||||
expect(component.text().includes('Live until 11:17')).toBeTruthy();
|
||||
expect(component.text().includes("Live until 11:17")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders live time remaining when displayLiveTimeRemaining is truthy', () => {
|
||||
it("renders live time remaining when displayLiveTimeRemaining is truthy", () => {
|
||||
// mock for stable snapshot
|
||||
jest.spyOn(Date, 'now').mockReturnValue(123456789);
|
||||
const beacon = new Beacon(makeBeaconInfoEvent('@user:server', '!room:server', { isLive: false }, '$1'));
|
||||
jest.spyOn(Date, "now").mockReturnValue(123456789);
|
||||
const beacon = new Beacon(makeBeaconInfoEvent("@user:server", "!room:server", { isLive: false }, "$1"));
|
||||
const component = getComponent({
|
||||
beacon, displayStatus: BeaconDisplayStatus.Active,
|
||||
beacon,
|
||||
displayStatus: BeaconDisplayStatus.Active,
|
||||
displayLiveTimeRemaining: true,
|
||||
});
|
||||
expect(component.text().includes('1h left')).toBeTruthy();
|
||||
expect(component.text().includes("1h left")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,21 +14,15 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount, ReactWrapper } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import {
|
||||
MatrixClient,
|
||||
MatrixEvent,
|
||||
Room,
|
||||
RoomMember,
|
||||
getBeaconInfoIdentifier,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { mount, ReactWrapper } from "enzyme";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { MatrixClient, MatrixEvent, Room, RoomMember, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
|
||||
import maplibregl from "maplibre-gl";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import BeaconViewDialog from '../../../../src/components/views/beacon/BeaconViewDialog';
|
||||
import BeaconViewDialog from "../../../../src/components/views/beacon/BeaconViewDialog";
|
||||
import {
|
||||
findByAttr,
|
||||
findByTestId,
|
||||
@@ -37,26 +31,26 @@ import {
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithBeacons,
|
||||
makeRoomWithStateEvents,
|
||||
} from '../../../test-utils';
|
||||
import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils';
|
||||
import { OwnBeaconStore } from '../../../../src/stores/OwnBeaconStore';
|
||||
import { BeaconDisplayStatus } from '../../../../src/components/views/beacon/displayStatus';
|
||||
import BeaconListItem from '../../../../src/components/views/beacon/BeaconListItem';
|
||||
} from "../../../test-utils";
|
||||
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
||||
import { OwnBeaconStore } from "../../../../src/stores/OwnBeaconStore";
|
||||
import { BeaconDisplayStatus } from "../../../../src/components/views/beacon/displayStatus";
|
||||
import BeaconListItem from "../../../../src/components/views/beacon/BeaconListItem";
|
||||
|
||||
describe('<BeaconViewDialog />', () => {
|
||||
describe("<BeaconViewDialog />", () => {
|
||||
// 14.03.2022 16:15
|
||||
const now = 1647270879403;
|
||||
// stable date for snapshots
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(now);
|
||||
const roomId = '!room:server';
|
||||
const aliceId = '@alice:server';
|
||||
const bobId = '@bob:server';
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
const roomId = "!room:server";
|
||||
const aliceId = "@alice:server";
|
||||
const bobId = "@bob:server";
|
||||
|
||||
const aliceMember = new RoomMember(roomId, aliceId);
|
||||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getClientWellKnown: jest.fn().mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
}),
|
||||
getUserId: jest.fn().mockReturnValue(bobId),
|
||||
getRoom: jest.fn(),
|
||||
@@ -70,20 +64,18 @@ describe('<BeaconViewDialog />', () => {
|
||||
// as we update room state
|
||||
const setupRoom = (stateEvents: MatrixEvent[] = []): Room => {
|
||||
const room1 = makeRoomWithStateEvents(stateEvents, { roomId, mockClient });
|
||||
jest.spyOn(room1, 'getMember').mockReturnValue(aliceMember);
|
||||
jest.spyOn(room1, "getMember").mockReturnValue(aliceMember);
|
||||
|
||||
return room1;
|
||||
};
|
||||
|
||||
const defaultEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
const defaultEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
|
||||
const location1 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: defaultEvent.getId(), geoUri: 'geo:51,41', timestamp: now + 1 },
|
||||
);
|
||||
const location1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: defaultEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
timestamp: now + 1,
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
onFinished: jest.fn(),
|
||||
@@ -91,74 +83,73 @@ describe('<BeaconViewDialog />', () => {
|
||||
matrixClient: mockClient as MatrixClient,
|
||||
};
|
||||
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<BeaconViewDialog {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => mount(<BeaconViewDialog {...defaultProps} {...props} />);
|
||||
|
||||
const openSidebar = (component: ReactWrapper) => act(() => {
|
||||
findByTestId(component, 'beacon-view-dialog-open-sidebar').at(0).simulate('click');
|
||||
component.setProps({});
|
||||
});
|
||||
const openSidebar = (component: ReactWrapper) =>
|
||||
act(() => {
|
||||
findByTestId(component, "beacon-view-dialog-open-sidebar").at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
maplibregl.AttributionControl = jest.fn();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(OwnBeaconStore.instance, 'getLiveBeaconIds').mockRestore();
|
||||
jest.spyOn(OwnBeaconStore.instance, 'getBeaconById').mockRestore();
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(now);
|
||||
jest.spyOn(OwnBeaconStore.instance, "getLiveBeaconIds").mockRestore();
|
||||
jest.spyOn(OwnBeaconStore.instance, "getBeaconById").mockRestore();
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders a map with markers', () => {
|
||||
it("renders a map with markers", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
const component = getComponent();
|
||||
expect(component.find('Map').props()).toEqual(expect.objectContaining({
|
||||
centerGeoUri: 'geo:51,41',
|
||||
interactive: true,
|
||||
}));
|
||||
expect(component.find('SmartMarker').length).toEqual(1);
|
||||
expect(component.find("Map").props()).toEqual(
|
||||
expect.objectContaining({
|
||||
centerGeoUri: "geo:51,41",
|
||||
interactive: true,
|
||||
}),
|
||||
);
|
||||
expect(component.find("SmartMarker").length).toEqual(1);
|
||||
});
|
||||
|
||||
it('does not render any own beacon status when user is not live sharing', () => {
|
||||
it("does not render any own beacon status when user is not live sharing", () => {
|
||||
// default event belongs to alice, we are bob
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
const component = getComponent();
|
||||
expect(component.find('DialogOwnBeaconStatus').html()).toBeNull();
|
||||
expect(component.find("DialogOwnBeaconStatus").html()).toBeNull();
|
||||
});
|
||||
|
||||
it('renders own beacon status when user is live sharing', () => {
|
||||
it("renders own beacon status when user is live sharing", () => {
|
||||
// default event belongs to alice
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
// mock own beacon store to show default event as alice's live beacon
|
||||
jest.spyOn(OwnBeaconStore.instance, 'getLiveBeaconIds').mockReturnValue([beacon.identifier]);
|
||||
jest.spyOn(OwnBeaconStore.instance, 'getBeaconById').mockReturnValue(beacon);
|
||||
jest.spyOn(OwnBeaconStore.instance, "getLiveBeaconIds").mockReturnValue([beacon.identifier]);
|
||||
jest.spyOn(OwnBeaconStore.instance, "getBeaconById").mockReturnValue(beacon);
|
||||
const component = getComponent();
|
||||
expect(component.find('MemberAvatar').length).toBeTruthy();
|
||||
expect(component.find('OwnBeaconStatus').props()).toEqual({
|
||||
beacon, displayStatus: BeaconDisplayStatus.Active,
|
||||
className: 'mx_DialogOwnBeaconStatus_status',
|
||||
expect(component.find("MemberAvatar").length).toBeTruthy();
|
||||
expect(component.find("OwnBeaconStatus").props()).toEqual({
|
||||
beacon,
|
||||
displayStatus: BeaconDisplayStatus.Active,
|
||||
className: "mx_DialogOwnBeaconStatus_status",
|
||||
});
|
||||
});
|
||||
|
||||
it('updates markers on changes to beacons', () => {
|
||||
it("updates markers on changes to beacons", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
const component = getComponent();
|
||||
expect(component.find('BeaconMarker').length).toEqual(1);
|
||||
expect(component.find("BeaconMarker").length).toEqual(1);
|
||||
|
||||
const anotherBeaconEvent = makeBeaconInfoEvent(bobId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$bob-room1-1',
|
||||
);
|
||||
const anotherBeaconEvent = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-1");
|
||||
|
||||
act(() => {
|
||||
// emits RoomStateEvent.BeaconLiveness
|
||||
@@ -168,21 +159,17 @@ describe('<BeaconViewDialog />', () => {
|
||||
component.setProps({});
|
||||
|
||||
// two markers now!
|
||||
expect(component.find('BeaconMarker').length).toEqual(2);
|
||||
expect(component.find("BeaconMarker").length).toEqual(2);
|
||||
});
|
||||
|
||||
it('does not update bounds or center on changing beacons', () => {
|
||||
it("does not update bounds or center on changing beacons", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
const component = getComponent();
|
||||
expect(component.find('BeaconMarker').length).toEqual(1);
|
||||
expect(component.find("BeaconMarker").length).toEqual(1);
|
||||
|
||||
const anotherBeaconEvent = makeBeaconInfoEvent(bobId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$bob-room1-1',
|
||||
);
|
||||
const anotherBeaconEvent = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-1");
|
||||
|
||||
act(() => {
|
||||
// emits RoomStateEvent.BeaconLiveness
|
||||
@@ -196,7 +183,7 @@ describe('<BeaconViewDialog />', () => {
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('renders a fallback when there are no locations', () => {
|
||||
it("renders a fallback when there are no locations", () => {
|
||||
// this is a cornercase, should not be a reachable state in UI anymore
|
||||
const onFinished = jest.fn();
|
||||
const room = setupRoom([defaultEvent]);
|
||||
@@ -204,30 +191,26 @@ describe('<BeaconViewDialog />', () => {
|
||||
const component = getComponent({ onFinished });
|
||||
|
||||
// map placeholder
|
||||
expect(findByTestId(component, 'beacon-view-dialog-map-fallback')).toMatchSnapshot();
|
||||
expect(findByTestId(component, "beacon-view-dialog-map-fallback")).toMatchSnapshot();
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'beacon-view-dialog-fallback-close').at(0).simulate('click');
|
||||
findByTestId(component, "beacon-view-dialog-fallback-close").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders map without markers when no live beacons remain', () => {
|
||||
it("renders map without markers when no live beacons remain", () => {
|
||||
const onFinished = jest.fn();
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
const component = getComponent({ onFinished });
|
||||
expect(component.find('BeaconMarker').length).toEqual(1);
|
||||
expect(component.find("BeaconMarker").length).toEqual(1);
|
||||
|
||||
// this will replace the defaultEvent
|
||||
// leading to no more live beacons
|
||||
const anotherBeaconEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: false },
|
||||
'$alice-room1-2',
|
||||
);
|
||||
const anotherBeaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: false }, "$alice-room1-2");
|
||||
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 51, lon: 41 });
|
||||
// reset call counts
|
||||
@@ -242,16 +225,16 @@ describe('<BeaconViewDialog />', () => {
|
||||
component.setProps({});
|
||||
|
||||
// no more avatars
|
||||
expect(component.find('MemberAvatar').length).toBeFalsy();
|
||||
expect(component.find("MemberAvatar").length).toBeFalsy();
|
||||
// map still rendered
|
||||
expect(component.find('Map').length).toBeTruthy();
|
||||
expect(component.find("Map").length).toBeTruthy();
|
||||
// map location unchanged
|
||||
expect(mockMap.setCenter).not.toHaveBeenCalled();
|
||||
expect(mockMap.fitBounds).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('sidebar', () => {
|
||||
it('opens sidebar on view list button click', () => {
|
||||
describe("sidebar", () => {
|
||||
it("opens sidebar on view list button click", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
@@ -259,10 +242,10 @@ describe('<BeaconViewDialog />', () => {
|
||||
|
||||
openSidebar(component);
|
||||
|
||||
expect(component.find('DialogSidebar').length).toBeTruthy();
|
||||
expect(component.find("DialogSidebar").length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('closes sidebar on close button click', () => {
|
||||
it("closes sidebar on close button click", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
@@ -271,34 +254,35 @@ describe('<BeaconViewDialog />', () => {
|
||||
// open the sidebar
|
||||
openSidebar(component);
|
||||
|
||||
expect(component.find('DialogSidebar').length).toBeTruthy();
|
||||
expect(component.find("DialogSidebar").length).toBeTruthy();
|
||||
|
||||
// now close it
|
||||
act(() => {
|
||||
findByAttr('data-testid')(component, 'dialog-sidebar-close').at(0).simulate('click');
|
||||
findByAttr("data-testid")(component, "dialog-sidebar-close").at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
expect(component.find('DialogSidebar').length).toBeFalsy();
|
||||
expect(component.find("DialogSidebar").length).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('focused beacons', () => {
|
||||
const beacon2Event = makeBeaconInfoEvent(bobId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$bob-room1-2',
|
||||
);
|
||||
describe("focused beacons", () => {
|
||||
const beacon2Event = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-2");
|
||||
|
||||
const location2 = makeBeaconEvent(
|
||||
bobId, { beaconInfoId: beacon2Event.getId(), geoUri: 'geo:33,22', timestamp: now + 1 },
|
||||
);
|
||||
const location2 = makeBeaconEvent(bobId, {
|
||||
beaconInfoId: beacon2Event.getId(),
|
||||
geoUri: "geo:33,22",
|
||||
timestamp: now + 1,
|
||||
});
|
||||
|
||||
const fitBoundsOptions = { maxZoom: 15, padding: 100 };
|
||||
|
||||
it('opens map with both beacons in view on first load without initialFocusedBeacon', () => {
|
||||
it("opens map with both beacons in view on first load without initialFocusedBeacon", () => {
|
||||
const [beacon1, beacon2] = makeRoomWithBeacons(
|
||||
roomId, mockClient, [defaultEvent, beacon2Event], [location1, location2],
|
||||
roomId,
|
||||
mockClient,
|
||||
[defaultEvent, beacon2Event],
|
||||
[location1, location2],
|
||||
);
|
||||
|
||||
getComponent({ beacons: [beacon1, beacon2] });
|
||||
@@ -308,15 +292,19 @@ describe('<BeaconViewDialog />', () => {
|
||||
// only called once
|
||||
expect(mockMap.setCenter).toHaveBeenCalledTimes(1);
|
||||
// bounds fit both beacons, only called once
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(new maplibregl.LngLatBounds(
|
||||
[22, 33], [41, 51],
|
||||
), fitBoundsOptions);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(
|
||||
new maplibregl.LngLatBounds([22, 33], [41, 51]),
|
||||
fitBoundsOptions,
|
||||
);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('opens map with both beacons in view on first load with an initially focused beacon', () => {
|
||||
it("opens map with both beacons in view on first load with an initially focused beacon", () => {
|
||||
const [beacon1, beacon2] = makeRoomWithBeacons(
|
||||
roomId, mockClient, [defaultEvent, beacon2Event], [location1, location2],
|
||||
roomId,
|
||||
mockClient,
|
||||
[defaultEvent, beacon2Event],
|
||||
[location1, location2],
|
||||
);
|
||||
|
||||
getComponent({ beacons: [beacon1, beacon2], initialFocusedBeacon: beacon1 });
|
||||
@@ -326,15 +314,19 @@ describe('<BeaconViewDialog />', () => {
|
||||
// only called once
|
||||
expect(mockMap.setCenter).toHaveBeenCalledTimes(1);
|
||||
// bounds fit both beacons, only called once
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(new maplibregl.LngLatBounds(
|
||||
[22, 33], [41, 51],
|
||||
), fitBoundsOptions);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(
|
||||
new maplibregl.LngLatBounds([22, 33], [41, 51]),
|
||||
fitBoundsOptions,
|
||||
);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('focuses on beacon location on sidebar list item click', () => {
|
||||
it("focuses on beacon location on sidebar list item click", () => {
|
||||
const [beacon1, beacon2] = makeRoomWithBeacons(
|
||||
roomId, mockClient, [defaultEvent, beacon2Event], [location1, location2],
|
||||
roomId,
|
||||
mockClient,
|
||||
[defaultEvent, beacon2Event],
|
||||
[location1, location2],
|
||||
);
|
||||
|
||||
const component = getComponent({ beacons: [beacon1, beacon2] });
|
||||
@@ -346,7 +338,7 @@ describe('<BeaconViewDialog />', () => {
|
||||
|
||||
act(() => {
|
||||
// click on the first beacon in the list
|
||||
component.find(BeaconListItem).at(0).simulate('click');
|
||||
component.find(BeaconListItem).at(0).simulate("click");
|
||||
});
|
||||
|
||||
// centered on clicked beacon
|
||||
@@ -354,16 +346,20 @@ describe('<BeaconViewDialog />', () => {
|
||||
// only called once
|
||||
expect(mockMap.setCenter).toHaveBeenCalledTimes(1);
|
||||
// bounds fitted just to clicked beacon
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(new maplibregl.LngLatBounds(
|
||||
[41, 51], [41, 51],
|
||||
), fitBoundsOptions);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(
|
||||
new maplibregl.LngLatBounds([41, 51], [41, 51]),
|
||||
fitBoundsOptions,
|
||||
);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('refocuses on same beacon when clicking list item again', () => {
|
||||
it("refocuses on same beacon when clicking list item again", () => {
|
||||
// test the map responds to refocusing the same beacon
|
||||
const [beacon1, beacon2] = makeRoomWithBeacons(
|
||||
roomId, mockClient, [defaultEvent, beacon2Event], [location1, location2],
|
||||
roomId,
|
||||
mockClient,
|
||||
[defaultEvent, beacon2Event],
|
||||
[location1, location2],
|
||||
);
|
||||
|
||||
const component = getComponent({ beacons: [beacon1, beacon2] });
|
||||
@@ -375,19 +371,17 @@ describe('<BeaconViewDialog />', () => {
|
||||
|
||||
act(() => {
|
||||
// click on the second beacon in the list
|
||||
component.find(BeaconListItem).at(1).simulate('click');
|
||||
component.find(BeaconListItem).at(1).simulate("click");
|
||||
});
|
||||
|
||||
const expectedBounds = new maplibregl.LngLatBounds(
|
||||
[22, 33], [22, 33],
|
||||
);
|
||||
const expectedBounds = new maplibregl.LngLatBounds([22, 33], [22, 33]);
|
||||
|
||||
// date is mocked but this relies on timestamp, manually mock a tick
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(now + 1);
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now + 1);
|
||||
|
||||
act(() => {
|
||||
// click on the second beacon in the list
|
||||
component.find(BeaconListItem).at(1).simulate('click');
|
||||
component.find(BeaconListItem).at(1).simulate("click");
|
||||
});
|
||||
|
||||
// centered on clicked beacon
|
||||
|
||||
@@ -14,21 +14,21 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import React from "react";
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import DialogSidebar from '../../../../src/components/views/beacon/DialogSidebar';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import DialogSidebar from "../../../../src/components/views/beacon/DialogSidebar";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
makeBeaconEvent,
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithBeacons,
|
||||
mockClientMethodsUser,
|
||||
} from '../../../test-utils';
|
||||
} from "../../../test-utils";
|
||||
|
||||
describe('<DialogSidebar />', () => {
|
||||
describe("<DialogSidebar />", () => {
|
||||
const defaultProps = {
|
||||
beacons: [],
|
||||
requestClose: jest.fn(),
|
||||
@@ -37,66 +37,66 @@ describe('<DialogSidebar />', () => {
|
||||
|
||||
const now = 1647270879403;
|
||||
|
||||
const roomId = '!room:server.org';
|
||||
const aliceId = '@alice:server.org';
|
||||
const roomId = "!room:server.org";
|
||||
const aliceId = "@alice:server.org";
|
||||
const client = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(aliceId),
|
||||
getRoom: jest.fn(),
|
||||
});
|
||||
|
||||
const beaconEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: true, timestamp: now },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
const location1 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: beaconEvent.getId(), geoUri: 'geo:51,41', timestamp: now },
|
||||
);
|
||||
const beaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true, timestamp: now }, "$alice-room1-1");
|
||||
const location1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: beaconEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
timestamp: now,
|
||||
});
|
||||
|
||||
const getComponent = (props = {}) => (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<DialogSidebar {...defaultProps} {...props} />);
|
||||
</MatrixClientContext.Provider>);
|
||||
<DialogSidebar {...defaultProps} {...props} />
|
||||
);
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
// mock now so time based text in snapshots is stable
|
||||
jest.spyOn(Date, 'now').mockReturnValue(now);
|
||||
jest.spyOn(Date, "now").mockReturnValue(now);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(Date, 'now').mockRestore();
|
||||
jest.spyOn(Date, "now").mockRestore();
|
||||
});
|
||||
|
||||
it('renders sidebar correctly without beacons', () => {
|
||||
it("renders sidebar correctly without beacons", () => {
|
||||
const { container } = render(getComponent());
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders sidebar correctly with beacons', () => {
|
||||
it("renders sidebar correctly with beacons", () => {
|
||||
const [beacon] = makeRoomWithBeacons(roomId, client, [beaconEvent], [location1]);
|
||||
const { container } = render(getComponent({ beacons: [beacon] }));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('calls on beacon click', () => {
|
||||
it("calls on beacon click", () => {
|
||||
const onBeaconClick = jest.fn();
|
||||
const [beacon] = makeRoomWithBeacons(roomId, client, [beaconEvent], [location1]);
|
||||
const { container } = render(getComponent({ beacons: [beacon], onBeaconClick }));
|
||||
|
||||
act(() => {
|
||||
const [listItem] = container.getElementsByClassName('mx_BeaconListItem');
|
||||
const [listItem] = container.getElementsByClassName("mx_BeaconListItem");
|
||||
fireEvent.click(listItem);
|
||||
});
|
||||
|
||||
expect(onBeaconClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('closes on close button click', () => {
|
||||
it("closes on close button click", () => {
|
||||
const requestClose = jest.fn();
|
||||
const { getByTestId } = render(getComponent({ requestClose }));
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByTestId('dialog-sidebar-close'));
|
||||
fireEvent.click(getByTestId("dialog-sidebar-close"));
|
||||
});
|
||||
expect(requestClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -14,19 +14,19 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mocked } from 'jest-mock';
|
||||
import React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { Beacon, BeaconIdentifier } from 'matrix-js-sdk/src/matrix';
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { Beacon, BeaconIdentifier } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import LeftPanelLiveShareWarning from '../../../../src/components/views/beacon/LeftPanelLiveShareWarning';
|
||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../../src/stores/OwnBeaconStore';
|
||||
import { flushPromises, makeBeaconInfoEvent } from '../../../test-utils';
|
||||
import dispatcher from '../../../../src/dispatcher/dispatcher';
|
||||
import { Action } from '../../../../src/dispatcher/actions';
|
||||
import LeftPanelLiveShareWarning from "../../../../src/components/views/beacon/LeftPanelLiveShareWarning";
|
||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../../../src/stores/OwnBeaconStore";
|
||||
import { flushPromises, makeBeaconInfoEvent } from "../../../test-utils";
|
||||
import dispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
|
||||
jest.mock('../../../../src/stores/OwnBeaconStore', () => {
|
||||
jest.mock("../../../../src/stores/OwnBeaconStore", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const EventEmitter = require("events");
|
||||
class MockOwnBeaconStore extends EventEmitter {
|
||||
@@ -38,57 +38,52 @@ jest.mock('../../../../src/stores/OwnBeaconStore', () => {
|
||||
}
|
||||
return {
|
||||
// @ts-ignore
|
||||
...jest.requireActual('../../../../src/stores/OwnBeaconStore'),
|
||||
...jest.requireActual("../../../../src/stores/OwnBeaconStore"),
|
||||
OwnBeaconStore: {
|
||||
instance: new MockOwnBeaconStore() as unknown as OwnBeaconStore,
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('<LeftPanelLiveShareWarning />', () => {
|
||||
describe("<LeftPanelLiveShareWarning />", () => {
|
||||
const getComponent = (props = {}) => render(<LeftPanelLiveShareWarning {...props} />);
|
||||
|
||||
const roomId1 = '!room1:server';
|
||||
const roomId2 = '!room2:server';
|
||||
const aliceId = '@alive:server';
|
||||
const roomId1 = "!room1:server";
|
||||
const roomId2 = "!room2:server";
|
||||
const aliceId = "@alive:server";
|
||||
|
||||
const now = 1647270879403;
|
||||
const HOUR_MS = 3600000;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(now);
|
||||
jest.spyOn(dispatcher, 'dispatch').mockClear().mockImplementation(() => { });
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
jest.spyOn(dispatcher, "dispatch")
|
||||
.mockClear()
|
||||
.mockImplementation(() => {});
|
||||
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.clear();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(global.Date, 'now').mockRestore();
|
||||
jest.spyOn(global.Date, "now").mockRestore();
|
||||
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
// 12h old, 12h left
|
||||
const beacon1 = new Beacon(makeBeaconInfoEvent(aliceId,
|
||||
roomId1,
|
||||
{ timeout: HOUR_MS * 24, timestamp: now - 12 * HOUR_MS },
|
||||
'$1',
|
||||
));
|
||||
const beacon1 = new Beacon(
|
||||
makeBeaconInfoEvent(aliceId, roomId1, { timeout: HOUR_MS * 24, timestamp: now - 12 * HOUR_MS }, "$1"),
|
||||
);
|
||||
// 10h left
|
||||
const beacon2 = new Beacon(makeBeaconInfoEvent(aliceId,
|
||||
roomId2,
|
||||
{ timeout: HOUR_MS * 10, timestamp: now },
|
||||
'$2',
|
||||
));
|
||||
const beacon2 = new Beacon(makeBeaconInfoEvent(aliceId, roomId2, { timeout: HOUR_MS * 10, timestamp: now }, "$2"));
|
||||
|
||||
it('renders nothing when user has no live beacons', () => {
|
||||
it("renders nothing when user has no live beacons", () => {
|
||||
const { container } = getComponent();
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('when user has live location monitor', () => {
|
||||
describe("when user has live location monitor", () => {
|
||||
beforeAll(() => {
|
||||
mocked(OwnBeaconStore.instance).getBeaconById.mockImplementation(beaconId => {
|
||||
mocked(OwnBeaconStore.instance).getBeaconById.mockImplementation((beaconId) => {
|
||||
if (beaconId === beacon1.identifier) {
|
||||
return beacon1;
|
||||
}
|
||||
@@ -104,17 +99,17 @@ describe('<LeftPanelLiveShareWarning />', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(document, 'addEventListener').mockRestore();
|
||||
jest.spyOn(document, "addEventListener").mockRestore();
|
||||
});
|
||||
|
||||
it('renders correctly when not minimized', () => {
|
||||
it("renders correctly when not minimized", () => {
|
||||
const { asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('goes to room of latest beacon when clicked', () => {
|
||||
it("goes to room of latest beacon when clicked", () => {
|
||||
const { container } = getComponent();
|
||||
const dispatchSpy = jest.spyOn(dispatcher, 'dispatch');
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
|
||||
fireEvent.click(container.querySelector("[role=button]"));
|
||||
|
||||
@@ -129,25 +124,25 @@ describe('<LeftPanelLiveShareWarning />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly when minimized', () => {
|
||||
it("renders correctly when minimized", () => {
|
||||
const { asFragment } = getComponent({ isMinimized: true });
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders location publish error', () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue(
|
||||
[beacon1.identifier],
|
||||
);
|
||||
it("renders location publish error", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
const { asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('goes to room of latest beacon with location publish error when clicked', () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue(
|
||||
[beacon1.identifier],
|
||||
);
|
||||
it("goes to room of latest beacon with location publish error when clicked", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
const { container } = getComponent();
|
||||
const dispatchSpy = jest.spyOn(dispatcher, 'dispatch');
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
|
||||
fireEvent.click(container.querySelector("[role=button]"));
|
||||
|
||||
@@ -162,30 +157,30 @@ describe('<LeftPanelLiveShareWarning />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('goes back to default style when wire errors are cleared', () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue(
|
||||
[beacon1.identifier],
|
||||
);
|
||||
it("goes back to default style when wire errors are cleared", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
const { container, rerender } = getComponent();
|
||||
// error mode
|
||||
expect(container.querySelector('.mx_LeftPanelLiveShareWarning').textContent).toEqual(
|
||||
'An error occurred whilst sharing your live location',
|
||||
expect(container.querySelector(".mx_LeftPanelLiveShareWarning").textContent).toEqual(
|
||||
"An error occurred whilst sharing your live location",
|
||||
);
|
||||
|
||||
act(() => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([]);
|
||||
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.LocationPublishError, 'abc');
|
||||
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.LocationPublishError, "abc");
|
||||
});
|
||||
|
||||
rerender(<LeftPanelLiveShareWarning />);
|
||||
|
||||
// default mode
|
||||
expect(container.querySelector('.mx_LeftPanelLiveShareWarning').textContent).toEqual(
|
||||
'You are sharing your live location',
|
||||
expect(container.querySelector(".mx_LeftPanelLiveShareWarning").textContent).toEqual(
|
||||
"You are sharing your live location",
|
||||
);
|
||||
});
|
||||
|
||||
it('removes itself when user stops having live beacons', async () => {
|
||||
it("removes itself when user stops having live beacons", async () => {
|
||||
const { container, rerender } = getComponent({ isMinimized: true });
|
||||
// started out rendered
|
||||
expect(container.innerHTML).toBeTruthy();
|
||||
@@ -202,14 +197,14 @@ describe('<LeftPanelLiveShareWarning />', () => {
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
it('refreshes beacon liveness monitors when pagevisibilty changes to visible', () => {
|
||||
it("refreshes beacon liveness monitors when pagevisibilty changes to visible", () => {
|
||||
OwnBeaconStore.instance.beacons.set(beacon1.identifier, beacon1);
|
||||
OwnBeaconStore.instance.beacons.set(beacon2.identifier, beacon2);
|
||||
const beacon1MonitorSpy = jest.spyOn(beacon1, 'monitorLiveness');
|
||||
const beacon2MonitorSpy = jest.spyOn(beacon1, 'monitorLiveness');
|
||||
const beacon1MonitorSpy = jest.spyOn(beacon1, "monitorLiveness");
|
||||
const beacon2MonitorSpy = jest.spyOn(beacon1, "monitorLiveness");
|
||||
|
||||
jest.spyOn(document, 'addEventListener').mockImplementation(
|
||||
(_e, listener) => (listener as EventListener)(new Event('')),
|
||||
jest.spyOn(document, "addEventListener").mockImplementation((_e, listener) =>
|
||||
(listener as EventListener)(new Event("")),
|
||||
);
|
||||
|
||||
expect(beacon1MonitorSpy).not.toHaveBeenCalled();
|
||||
@@ -220,42 +215,42 @@ describe('<LeftPanelLiveShareWarning />', () => {
|
||||
expect(beacon2MonitorSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('stopping errors', () => {
|
||||
it('renders stopping error', () => {
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error('error'));
|
||||
describe("stopping errors", () => {
|
||||
it("renders stopping error", () => {
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error("error"));
|
||||
const { container } = getComponent();
|
||||
expect(container.textContent).toEqual('An error occurred while stopping your live location');
|
||||
expect(container.textContent).toEqual("An error occurred while stopping your live location");
|
||||
});
|
||||
|
||||
it('starts rendering stopping error on beaconUpdateError emit', () => {
|
||||
it("starts rendering stopping error on beaconUpdateError emit", () => {
|
||||
const { container } = getComponent();
|
||||
// no error
|
||||
expect(container.textContent).toEqual('You are sharing your live location');
|
||||
expect(container.textContent).toEqual("You are sharing your live location");
|
||||
|
||||
act(() => {
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error('error'));
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error("error"));
|
||||
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.BeaconUpdateError, beacon2.identifier, true);
|
||||
});
|
||||
|
||||
expect(container.textContent).toEqual('An error occurred while stopping your live location');
|
||||
expect(container.textContent).toEqual("An error occurred while stopping your live location");
|
||||
});
|
||||
|
||||
it('renders stopping error when beacons have stopping and location errors', () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue(
|
||||
[beacon1.identifier],
|
||||
);
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error('error'));
|
||||
it("renders stopping error when beacons have stopping and location errors", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error("error"));
|
||||
const { container } = getComponent();
|
||||
expect(container.textContent).toEqual('An error occurred while stopping your live location');
|
||||
expect(container.textContent).toEqual("An error occurred while stopping your live location");
|
||||
});
|
||||
|
||||
it('goes to room of latest beacon with stopping error when clicked', () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue(
|
||||
[beacon1.identifier],
|
||||
);
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error('error'));
|
||||
it("goes to room of latest beacon with stopping error when clicked", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error("error"));
|
||||
const { container } = getComponent();
|
||||
const dispatchSpy = jest.spyOn(dispatcher, 'dispatch');
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
|
||||
fireEvent.click(container.querySelector("[role=button]"));
|
||||
|
||||
|
||||
@@ -14,62 +14,61 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { Beacon } from 'matrix-js-sdk/src/matrix';
|
||||
import { mount } from "enzyme";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { mocked } from "jest-mock";
|
||||
import { Beacon } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import OwnBeaconStatus from '../../../../src/components/views/beacon/OwnBeaconStatus';
|
||||
import { BeaconDisplayStatus } from '../../../../src/components/views/beacon/displayStatus';
|
||||
import { useOwnLiveBeacons } from '../../../../src/utils/beacon';
|
||||
import { findByTestId, makeBeaconInfoEvent } from '../../../test-utils';
|
||||
import OwnBeaconStatus from "../../../../src/components/views/beacon/OwnBeaconStatus";
|
||||
import { BeaconDisplayStatus } from "../../../../src/components/views/beacon/displayStatus";
|
||||
import { useOwnLiveBeacons } from "../../../../src/utils/beacon";
|
||||
import { findByTestId, makeBeaconInfoEvent } from "../../../test-utils";
|
||||
|
||||
jest.mock('../../../../src/utils/beacon/useOwnLiveBeacons', () => ({
|
||||
jest.mock("../../../../src/utils/beacon/useOwnLiveBeacons", () => ({
|
||||
useOwnLiveBeacons: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('<OwnBeaconStatus />', () => {
|
||||
describe("<OwnBeaconStatus />", () => {
|
||||
const defaultProps = {
|
||||
displayStatus: BeaconDisplayStatus.Loading,
|
||||
};
|
||||
const userId = '@user:server';
|
||||
const roomId = '!room:server';
|
||||
const userId = "@user:server";
|
||||
const roomId = "!room:server";
|
||||
let defaultBeacon;
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<OwnBeaconStatus {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => mount(<OwnBeaconStatus {...defaultProps} {...props} />);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(123456789);
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(123456789);
|
||||
mocked(useOwnLiveBeacons).mockClear().mockReturnValue({});
|
||||
|
||||
defaultBeacon = new Beacon(makeBeaconInfoEvent(userId, roomId));
|
||||
});
|
||||
|
||||
it('renders without a beacon instance', () => {
|
||||
it("renders without a beacon instance", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders loading state correctly', () => {
|
||||
it("renders loading state correctly", () => {
|
||||
const component = getComponent();
|
||||
expect(component.find('BeaconStatus').props()).toBeTruthy();
|
||||
expect(component.find("BeaconStatus").props()).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('Active state', () => {
|
||||
it('renders stop button', () => {
|
||||
describe("Active state", () => {
|
||||
it("renders stop button", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
onStopSharing: jest.fn(),
|
||||
});
|
||||
const component = getComponent({ displayStatus, beacon: defaultBeacon });
|
||||
expect(component.text()).toContain('Live location enabled');
|
||||
expect(component.text()).toContain("Live location enabled");
|
||||
|
||||
expect(findByTestId(component, 'beacon-status-stop-beacon').length).toBeTruthy();
|
||||
expect(findByTestId(component, "beacon-status-stop-beacon").length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('stops sharing on stop button click', () => {
|
||||
it("stops sharing on stop button click", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
const onStopSharing = jest.fn();
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
@@ -78,37 +77,37 @@ describe('<OwnBeaconStatus />', () => {
|
||||
const component = getComponent({ displayStatus, beacon: defaultBeacon });
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'beacon-status-stop-beacon').at(0).simulate('click');
|
||||
findByTestId(component, "beacon-status-stop-beacon").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(onStopSharing).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('renders in error mode when displayStatus is error', () => {
|
||||
describe("errors", () => {
|
||||
it("renders in error mode when displayStatus is error", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Error;
|
||||
const component = getComponent({ displayStatus });
|
||||
expect(component.text()).toEqual('Live location error');
|
||||
expect(component.text()).toEqual("Live location error");
|
||||
|
||||
// no actions for plain error
|
||||
expect(component.find('AccessibleButton').length).toBeFalsy();
|
||||
expect(component.find("AccessibleButton").length).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('with location publish error', () => {
|
||||
it('renders in error mode', () => {
|
||||
describe("with location publish error", () => {
|
||||
it("renders in error mode", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
hasLocationPublishError: true,
|
||||
onResetLocationPublishError: jest.fn(),
|
||||
});
|
||||
const component = getComponent({ displayStatus, beacon: defaultBeacon });
|
||||
expect(component.text()).toContain('Live location error');
|
||||
expect(component.text()).toContain("Live location error");
|
||||
// retry button
|
||||
expect(findByTestId(component, 'beacon-status-reset-wire-error').length).toBeTruthy();
|
||||
expect(findByTestId(component, "beacon-status-reset-wire-error").length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('retry button resets location publish error', () => {
|
||||
it("retry button resets location publish error", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
const onResetLocationPublishError = jest.fn();
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
@@ -117,15 +116,15 @@ describe('<OwnBeaconStatus />', () => {
|
||||
});
|
||||
const component = getComponent({ displayStatus, beacon: defaultBeacon });
|
||||
act(() => {
|
||||
findByTestId(component, 'beacon-status-reset-wire-error').at(0).simulate('click');
|
||||
findByTestId(component, "beacon-status-reset-wire-error").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(onResetLocationPublishError).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with stopping error', () => {
|
||||
it('renders in error mode', () => {
|
||||
describe("with stopping error", () => {
|
||||
it("renders in error mode", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
hasLocationPublishError: false,
|
||||
@@ -133,12 +132,12 @@ describe('<OwnBeaconStatus />', () => {
|
||||
onStopSharing: jest.fn(),
|
||||
});
|
||||
const component = getComponent({ displayStatus, beacon: defaultBeacon });
|
||||
expect(component.text()).toContain('Live location error');
|
||||
expect(component.text()).toContain("Live location error");
|
||||
// retry button
|
||||
expect(findByTestId(component, 'beacon-status-stop-beacon-retry').length).toBeTruthy();
|
||||
expect(findByTestId(component, "beacon-status-stop-beacon-retry").length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('retry button retries stop sharing', () => {
|
||||
it("retry button retries stop sharing", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
const onStopSharing = jest.fn();
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
@@ -147,7 +146,7 @@ describe('<OwnBeaconStatus />', () => {
|
||||
});
|
||||
const component = getComponent({ displayStatus, beacon: defaultBeacon });
|
||||
act(() => {
|
||||
findByTestId(component, 'beacon-status-stop-beacon-retry').at(0).simulate('click');
|
||||
findByTestId(component, "beacon-status-stop-beacon-retry").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(onStopSharing).toHaveBeenCalled();
|
||||
@@ -155,7 +154,7 @@ describe('<OwnBeaconStatus />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('renders loading state correctly', () => {
|
||||
it("renders loading state correctly", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -16,28 +16,12 @@ limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import {
|
||||
Room,
|
||||
PendingEventOrdering,
|
||||
MatrixClient,
|
||||
RoomMember,
|
||||
RoomStateEvent,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { Room, PendingEventOrdering, MatrixClient, RoomMember, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { ClientWidgetApi, Widget } from "matrix-widget-api";
|
||||
import {
|
||||
cleanup,
|
||||
render,
|
||||
screen,
|
||||
} from "@testing-library/react";
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
import { mocked, Mocked } from "jest-mock";
|
||||
|
||||
import {
|
||||
mkRoomMember,
|
||||
MockedCall,
|
||||
setupAsyncStoreWithClient,
|
||||
stubClient,
|
||||
useMockedCalls,
|
||||
} from "../../../test-utils";
|
||||
import { mkRoomMember, MockedCall, setupAsyncStoreWithClient, stubClient, useMockedCalls } from "../../../test-utils";
|
||||
import RoomCallBanner from "../../../../src/components/views/beacon/RoomCallBanner";
|
||||
import { CallStore } from "../../../../src/stores/CallStore";
|
||||
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
|
||||
@@ -64,13 +48,9 @@ describe("<RoomCallBanner />", () => {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
alice = mkRoomMember(room.roomId, "@alice:example.org");
|
||||
jest.spyOn(room, "getMember").mockImplementation((userId) =>
|
||||
userId === alice.userId ? alice : null,
|
||||
);
|
||||
jest.spyOn(room, "getMember").mockImplementation((userId) => (userId === alice.userId ? alice : null));
|
||||
|
||||
client.getRoom.mockImplementation((roomId) =>
|
||||
roomId === room.roomId ? room : null,
|
||||
);
|
||||
client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
|
||||
client.getRooms.mockReturnValue([room]);
|
||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||
|
||||
@@ -100,7 +80,9 @@ describe("<RoomCallBanner />", () => {
|
||||
beforeEach(() => {
|
||||
MockedCall.create(room, "1");
|
||||
const maybeCall = CallStore.instance.getCall(room.roomId);
|
||||
if (!(maybeCall instanceof MockedCall)) {throw new Error("Failed to create call");}
|
||||
if (!(maybeCall instanceof MockedCall)) {
|
||||
throw new Error("Failed to create call");
|
||||
}
|
||||
call = maybeCall;
|
||||
|
||||
widget = new Widget(call.widget);
|
||||
|
||||
@@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import React from "react";
|
||||
import { act } from "react-dom/test-utils";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { Room, Beacon, BeaconEvent, getBeaconInfoIdentifier } from 'matrix-js-sdk/src/matrix';
|
||||
import { logger } from 'matrix-js-sdk/src/logger';
|
||||
import { mount } from "enzyme";
|
||||
import { Room, Beacon, BeaconEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import RoomLiveShareWarning from '../../../../src/components/views/beacon/RoomLiveShareWarning';
|
||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../../src/stores/OwnBeaconStore';
|
||||
import RoomLiveShareWarning from "../../../../src/components/views/beacon/RoomLiveShareWarning";
|
||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../../../src/stores/OwnBeaconStore";
|
||||
import {
|
||||
advanceDateAndTime,
|
||||
findByTestId,
|
||||
@@ -32,20 +32,20 @@ import {
|
||||
mockGeolocation,
|
||||
resetAsyncStoreWithClient,
|
||||
setupAsyncStoreWithClient,
|
||||
} from '../../../test-utils';
|
||||
import defaultDispatcher from '../../../../src/dispatcher/dispatcher';
|
||||
import { Action } from '../../../../src/dispatcher/actions';
|
||||
} from "../../../test-utils";
|
||||
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
|
||||
jest.useFakeTimers();
|
||||
describe('<RoomLiveShareWarning />', () => {
|
||||
const aliceId = '@alice:server.org';
|
||||
const room1Id = '$room1:server.org';
|
||||
const room2Id = '$room2:server.org';
|
||||
const room3Id = '$room3:server.org';
|
||||
describe("<RoomLiveShareWarning />", () => {
|
||||
const aliceId = "@alice:server.org";
|
||||
const room1Id = "$room1:server.org";
|
||||
const room2Id = "$room2:server.org";
|
||||
const room3Id = "$room3:server.org";
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getVisibleRooms: jest.fn().mockReturnValue([]),
|
||||
getUserId: jest.fn().mockReturnValue(aliceId),
|
||||
unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }),
|
||||
unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }),
|
||||
sendEvent: jest.fn(),
|
||||
});
|
||||
|
||||
@@ -54,14 +54,19 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
const MINUTE_MS = 60000;
|
||||
const HOUR_MS = 3600000;
|
||||
// mock the date so events are stable for snapshots etc
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(now);
|
||||
const room1Beacon1 = makeBeaconInfoEvent(aliceId, room1Id, {
|
||||
isLive: true,
|
||||
timeout: HOUR_MS,
|
||||
}, '$0');
|
||||
const room2Beacon1 = makeBeaconInfoEvent(aliceId, room2Id, { isLive: true, timeout: HOUR_MS }, '$1');
|
||||
const room2Beacon2 = makeBeaconInfoEvent(aliceId, room2Id, { isLive: true, timeout: HOUR_MS * 12 }, '$2');
|
||||
const room3Beacon1 = makeBeaconInfoEvent(aliceId, room3Id, { isLive: true, timeout: HOUR_MS }, '$3');
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
const room1Beacon1 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
room1Id,
|
||||
{
|
||||
isLive: true,
|
||||
timeout: HOUR_MS,
|
||||
},
|
||||
"$0",
|
||||
);
|
||||
const room2Beacon1 = makeBeaconInfoEvent(aliceId, room2Id, { isLive: true, timeout: HOUR_MS }, "$1");
|
||||
const room2Beacon2 = makeBeaconInfoEvent(aliceId, room2Id, { isLive: true, timeout: HOUR_MS * 12 }, "$2");
|
||||
const room3Beacon1 = makeBeaconInfoEvent(aliceId, room3Id, { isLive: true, timeout: HOUR_MS }, "$3");
|
||||
|
||||
// make fresh rooms every time
|
||||
// as we update room state
|
||||
@@ -96,42 +101,39 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
return component;
|
||||
};
|
||||
|
||||
const localStorageSpy = jest.spyOn(localStorage.__proto__, 'getItem').mockReturnValue(undefined);
|
||||
const localStorageSpy = jest.spyOn(localStorage.__proto__, "getItem").mockReturnValue(undefined);
|
||||
|
||||
beforeEach(() => {
|
||||
mockGeolocation();
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(now);
|
||||
mockClient.unstable_setLiveBeacon.mockReset().mockResolvedValue({ event_id: '1' });
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
mockClient.unstable_setLiveBeacon.mockReset().mockResolvedValue({ event_id: "1" });
|
||||
|
||||
// assume all beacons were created on this device
|
||||
localStorageSpy.mockReturnValue(JSON.stringify([
|
||||
room1Beacon1.getId(),
|
||||
room2Beacon1.getId(),
|
||||
room2Beacon2.getId(),
|
||||
room3Beacon1.getId(),
|
||||
]));
|
||||
localStorageSpy.mockReturnValue(
|
||||
JSON.stringify([room1Beacon1.getId(), room2Beacon1.getId(), room2Beacon2.getId(), room3Beacon1.getId()]),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
jest.spyOn(OwnBeaconStore.instance, 'beaconHasLocationPublishError').mockRestore();
|
||||
jest.spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError").mockRestore();
|
||||
await resetAsyncStoreWithClient(OwnBeaconStore.instance);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(global.Date, 'now').mockRestore();
|
||||
jest.spyOn(global.Date, "now").mockRestore();
|
||||
localStorageSpy.mockRestore();
|
||||
jest.spyOn(defaultDispatcher, 'dispatch').mockRestore();
|
||||
jest.spyOn(defaultDispatcher, "dispatch").mockRestore();
|
||||
});
|
||||
|
||||
const getExpiryText = wrapper => findByTestId(wrapper, 'room-live-share-expiry').text();
|
||||
const getExpiryText = (wrapper) => findByTestId(wrapper, "room-live-share-expiry").text();
|
||||
|
||||
it('renders nothing when user has no live beacons at all', async () => {
|
||||
it("renders nothing when user has no live beacons at all", async () => {
|
||||
await makeOwnBeaconStore();
|
||||
const component = getComponent();
|
||||
expect(component.html()).toBe(null);
|
||||
});
|
||||
|
||||
it('renders nothing when user has no live beacons in room', async () => {
|
||||
it("renders nothing when user has no live beacons in room", async () => {
|
||||
await act(async () => {
|
||||
await makeRoomsWithStateEvents([room2Beacon1]);
|
||||
await makeOwnBeaconStore();
|
||||
@@ -140,8 +142,8 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
expect(component.html()).toBe(null);
|
||||
});
|
||||
|
||||
it('does not render when geolocation is not working', async () => {
|
||||
jest.spyOn(logger, 'error').mockImplementation(() => { });
|
||||
it("does not render when geolocation is not working", async () => {
|
||||
jest.spyOn(logger, "error").mockImplementation(() => {});
|
||||
// @ts-ignore
|
||||
navigator.geolocation = undefined;
|
||||
await act(async () => {
|
||||
@@ -155,7 +157,7 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
expect(component.html()).toBeNull();
|
||||
});
|
||||
|
||||
describe('when user has live beacons and geolocation is available', () => {
|
||||
describe("when user has live beacons and geolocation is available", () => {
|
||||
beforeEach(async () => {
|
||||
await act(async () => {
|
||||
await makeRoomsWithStateEvents([room1Beacon1, room2Beacon1, room2Beacon2]);
|
||||
@@ -163,23 +165,23 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly with one live beacon in room', () => {
|
||||
it("renders correctly with one live beacon in room", () => {
|
||||
const component = getComponent({ roomId: room1Id });
|
||||
// beacons have generated ids that break snapshots
|
||||
// assert on html
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders correctly with two live beacons in room', () => {
|
||||
it("renders correctly with two live beacons in room", () => {
|
||||
const component = getComponent({ roomId: room2Id });
|
||||
// beacons have generated ids that break snapshots
|
||||
// assert on html
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
// later expiry displayed
|
||||
expect(getExpiryText(component)).toEqual('12h left');
|
||||
expect(getExpiryText(component)).toEqual("12h left");
|
||||
});
|
||||
|
||||
it('removes itself when user stops having live beacons', async () => {
|
||||
it("removes itself when user stops having live beacons", async () => {
|
||||
const component = getComponent({ roomId: room1Id });
|
||||
// started out rendered
|
||||
expect(component.html()).toBeTruthy();
|
||||
@@ -196,7 +198,7 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
expect(component.html()).toBe(null);
|
||||
});
|
||||
|
||||
it('removes itself when user stops monitoring live position', async () => {
|
||||
it("removes itself when user stops monitoring live position", async () => {
|
||||
const component = getComponent({ roomId: room1Id });
|
||||
// started out rendered
|
||||
expect(component.html()).toBeTruthy();
|
||||
@@ -212,7 +214,7 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
expect(component.html()).toBe(null);
|
||||
});
|
||||
|
||||
it('renders when user adds a live beacon', async () => {
|
||||
it("renders when user adds a live beacon", async () => {
|
||||
const component = getComponent({ roomId: room3Id });
|
||||
// started out not rendered
|
||||
expect(component.html()).toBeFalsy();
|
||||
@@ -225,40 +227,45 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
expect(component.html()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('updates beacon time left periodically', () => {
|
||||
it("updates beacon time left periodically", () => {
|
||||
const component = getComponent({ roomId: room1Id });
|
||||
expect(getExpiryText(component)).toEqual('1h left');
|
||||
expect(getExpiryText(component)).toEqual("1h left");
|
||||
|
||||
act(() => {
|
||||
advanceDateAndTime(MINUTE_MS * 25);
|
||||
});
|
||||
|
||||
expect(getExpiryText(component)).toEqual('35m left');
|
||||
expect(getExpiryText(component)).toEqual("35m left");
|
||||
});
|
||||
|
||||
it('updates beacon time left when beacon updates', () => {
|
||||
it("updates beacon time left when beacon updates", () => {
|
||||
const component = getComponent({ roomId: room1Id });
|
||||
expect(getExpiryText(component)).toEqual('1h left');
|
||||
expect(getExpiryText(component)).toEqual("1h left");
|
||||
|
||||
expect(getExpiryText(component)).toEqual('1h left');
|
||||
expect(getExpiryText(component)).toEqual("1h left");
|
||||
|
||||
act(() => {
|
||||
const beacon = OwnBeaconStore.instance.getBeaconById(getBeaconInfoIdentifier(room1Beacon1));
|
||||
const room1Beacon1Update = makeBeaconInfoEvent(aliceId, room1Id, {
|
||||
isLive: true,
|
||||
timeout: 3 * HOUR_MS,
|
||||
}, '$0');
|
||||
const room1Beacon1Update = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
room1Id,
|
||||
{
|
||||
isLive: true,
|
||||
timeout: 3 * HOUR_MS,
|
||||
},
|
||||
"$0",
|
||||
);
|
||||
beacon.update(room1Beacon1Update);
|
||||
});
|
||||
|
||||
// update to expiry of new beacon
|
||||
expect(getExpiryText(component)).toEqual('3h left');
|
||||
expect(getExpiryText(component)).toEqual("3h left");
|
||||
});
|
||||
|
||||
it('clears expiry time interval on unmount', () => {
|
||||
const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
|
||||
it("clears expiry time interval on unmount", () => {
|
||||
const clearIntervalSpy = jest.spyOn(global, "clearInterval");
|
||||
const component = getComponent({ roomId: room1Id });
|
||||
expect(getExpiryText(component)).toEqual('1h left');
|
||||
expect(getExpiryText(component)).toEqual("1h left");
|
||||
|
||||
act(() => {
|
||||
component.unmount();
|
||||
@@ -267,12 +274,12 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
expect(clearIntervalSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('navigates to beacon tile on click', () => {
|
||||
const dispatcherSpy = jest.spyOn(defaultDispatcher, 'dispatch');
|
||||
it("navigates to beacon tile on click", () => {
|
||||
const dispatcherSpy = jest.spyOn(defaultDispatcher, "dispatch");
|
||||
const component = getComponent({ roomId: room1Id });
|
||||
|
||||
act(() => {
|
||||
component.simulate('click');
|
||||
component.simulate("click");
|
||||
});
|
||||
|
||||
expect(dispatcherSpy).toHaveBeenCalledWith({
|
||||
@@ -285,30 +292,30 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('stopping beacons', () => {
|
||||
it('stops beacon on stop sharing click', () => {
|
||||
describe("stopping beacons", () => {
|
||||
it("stops beacon on stop sharing click", () => {
|
||||
const component = getComponent({ roomId: room2Id });
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||
findByTestId(component, "room-live-share-primary-button").at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalled();
|
||||
expect(component.find('Spinner').length).toBeTruthy();
|
||||
expect(findByTestId(component, 'room-live-share-primary-button').at(0).props().disabled).toBeTruthy();
|
||||
expect(component.find("Spinner").length).toBeTruthy();
|
||||
expect(findByTestId(component, "room-live-share-primary-button").at(0).props().disabled).toBeTruthy();
|
||||
});
|
||||
|
||||
it('displays error when stop sharing fails', async () => {
|
||||
it("displays error when stop sharing fails", async () => {
|
||||
const component = getComponent({ roomId: room1Id });
|
||||
|
||||
// fail first time
|
||||
mockClient.unstable_setLiveBeacon
|
||||
.mockRejectedValueOnce(new Error('oups'))
|
||||
.mockResolvedValue(({ event_id: '1' }));
|
||||
.mockRejectedValueOnce(new Error("oups"))
|
||||
.mockResolvedValue({ event_id: "1" });
|
||||
|
||||
await act(async () => {
|
||||
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||
findByTestId(component, "room-live-share-primary-button").at(0).simulate("click");
|
||||
await flushPromisesWithFakeTimers();
|
||||
});
|
||||
component.setProps({});
|
||||
@@ -316,20 +323,20 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||
findByTestId(component, "room-live-share-primary-button").at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('displays again with correct state after stopping a beacon', () => {
|
||||
it("displays again with correct state after stopping a beacon", () => {
|
||||
// make sure the loading state is reset correctly after removing a beacon
|
||||
const component = getComponent({ roomId: room1Id });
|
||||
|
||||
// stop the beacon
|
||||
act(() => {
|
||||
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||
findByTestId(component, "room-live-share-primary-button").at(0).simulate("click");
|
||||
});
|
||||
// time travel until room1Beacon1 is expired
|
||||
act(() => {
|
||||
@@ -345,28 +352,30 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
});
|
||||
|
||||
// button not disabled and expiry time shown
|
||||
expect(findByTestId(component, 'room-live-share-primary-button').at(0).props().disabled).toBeFalsy();
|
||||
expect(findByTestId(component, 'room-live-share-expiry').text()).toEqual('1h left');
|
||||
expect(findByTestId(component, "room-live-share-primary-button").at(0).props().disabled).toBeFalsy();
|
||||
expect(findByTestId(component, "room-live-share-expiry").text()).toEqual("1h left");
|
||||
});
|
||||
});
|
||||
|
||||
describe('with location publish errors', () => {
|
||||
it('displays location publish error when mounted with location publish errors', async () => {
|
||||
const locationPublishErrorSpy = jest.spyOn(OwnBeaconStore.instance, 'beaconHasLocationPublishError')
|
||||
describe("with location publish errors", () => {
|
||||
it("displays location publish error when mounted with location publish errors", async () => {
|
||||
const locationPublishErrorSpy = jest
|
||||
.spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError")
|
||||
.mockReturnValue(true);
|
||||
const component = getComponent({ roomId: room2Id });
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(locationPublishErrorSpy).toHaveBeenCalledWith(
|
||||
getBeaconInfoIdentifier(room2Beacon1), 0, [getBeaconInfoIdentifier(room2Beacon1)],
|
||||
);
|
||||
expect(locationPublishErrorSpy).toHaveBeenCalledWith(getBeaconInfoIdentifier(room2Beacon1), 0, [
|
||||
getBeaconInfoIdentifier(room2Beacon1),
|
||||
]);
|
||||
});
|
||||
|
||||
it(
|
||||
'displays location publish error when locationPublishError event is emitted' +
|
||||
' and beacons have errors',
|
||||
"displays location publish error when locationPublishError event is emitted" +
|
||||
" and beacons have errors",
|
||||
async () => {
|
||||
const locationPublishErrorSpy = jest.spyOn(OwnBeaconStore.instance, 'beaconHasLocationPublishError')
|
||||
const locationPublishErrorSpy = jest
|
||||
.spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError")
|
||||
.mockReturnValue(false);
|
||||
const component = getComponent({ roomId: room2Id });
|
||||
|
||||
@@ -374,20 +383,23 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
act(() => {
|
||||
locationPublishErrorSpy.mockReturnValue(true);
|
||||
OwnBeaconStore.instance.emit(
|
||||
OwnBeaconStoreEvent.LocationPublishError, getBeaconInfoIdentifier(room2Beacon1),
|
||||
OwnBeaconStoreEvent.LocationPublishError,
|
||||
getBeaconInfoIdentifier(room2Beacon1),
|
||||
);
|
||||
});
|
||||
component.setProps({});
|
||||
|
||||
// renders wire error ui
|
||||
expect(component.find('.mx_RoomLiveShareWarning_label').text()).toEqual(
|
||||
'An error occurred whilst sharing your live location, please try again',
|
||||
expect(component.find(".mx_RoomLiveShareWarning_label").text()).toEqual(
|
||||
"An error occurred whilst sharing your live location, please try again",
|
||||
);
|
||||
expect(findByTestId(component, 'room-live-share-wire-error-close-button').length).toBeTruthy();
|
||||
});
|
||||
expect(findByTestId(component, "room-live-share-wire-error-close-button").length).toBeTruthy();
|
||||
},
|
||||
);
|
||||
|
||||
it('stops displaying wire error when errors are cleared', async () => {
|
||||
const locationPublishErrorSpy = jest.spyOn(OwnBeaconStore.instance, 'beaconHasLocationPublishError')
|
||||
it("stops displaying wire error when errors are cleared", async () => {
|
||||
const locationPublishErrorSpy = jest
|
||||
.spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError")
|
||||
.mockReturnValue(true);
|
||||
const component = getComponent({ roomId: room2Id });
|
||||
|
||||
@@ -395,39 +407,40 @@ describe('<RoomLiveShareWarning />', () => {
|
||||
act(() => {
|
||||
locationPublishErrorSpy.mockReturnValue(false);
|
||||
OwnBeaconStore.instance.emit(
|
||||
OwnBeaconStoreEvent.LocationPublishError, getBeaconInfoIdentifier(room2Beacon1),
|
||||
OwnBeaconStoreEvent.LocationPublishError,
|
||||
getBeaconInfoIdentifier(room2Beacon1),
|
||||
);
|
||||
});
|
||||
component.setProps({});
|
||||
|
||||
// renders error-free ui
|
||||
expect(component.find('.mx_RoomLiveShareWarning_label').text()).toEqual(
|
||||
'You are sharing your live location',
|
||||
expect(component.find(".mx_RoomLiveShareWarning_label").text()).toEqual(
|
||||
"You are sharing your live location",
|
||||
);
|
||||
expect(findByTestId(component, 'room-live-share-wire-error-close-button').length).toBeFalsy();
|
||||
expect(findByTestId(component, "room-live-share-wire-error-close-button").length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('clicking retry button resets location publish errors', async () => {
|
||||
jest.spyOn(OwnBeaconStore.instance, 'beaconHasLocationPublishError').mockReturnValue(true);
|
||||
const resetErrorSpy = jest.spyOn(OwnBeaconStore.instance, 'resetLocationPublishError');
|
||||
it("clicking retry button resets location publish errors", async () => {
|
||||
jest.spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError").mockReturnValue(true);
|
||||
const resetErrorSpy = jest.spyOn(OwnBeaconStore.instance, "resetLocationPublishError");
|
||||
|
||||
const component = getComponent({ roomId: room2Id });
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||
findByTestId(component, "room-live-share-primary-button").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(resetErrorSpy).toHaveBeenCalledWith(getBeaconInfoIdentifier(room2Beacon1));
|
||||
});
|
||||
|
||||
it('clicking close button stops beacons', async () => {
|
||||
jest.spyOn(OwnBeaconStore.instance, 'beaconHasLocationPublishError').mockReturnValue(true);
|
||||
const stopBeaconSpy = jest.spyOn(OwnBeaconStore.instance, 'stopBeacon');
|
||||
it("clicking close button stops beacons", async () => {
|
||||
jest.spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError").mockReturnValue(true);
|
||||
const stopBeaconSpy = jest.spyOn(OwnBeaconStore.instance, "stopBeacon");
|
||||
|
||||
const component = getComponent({ roomId: room2Id });
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'room-live-share-wire-error-close-button').at(0).simulate('click');
|
||||
findByTestId(component, "room-live-share-wire-error-close-button").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(stopBeaconSpy).toHaveBeenCalledWith(getBeaconInfoIdentifier(room2Beacon1));
|
||||
|
||||
@@ -14,21 +14,21 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
|
||||
import ShareLatestLocation from '../../../../src/components/views/beacon/ShareLatestLocation';
|
||||
import { copyPlaintext } from '../../../../src/utils/strings';
|
||||
import { flushPromises } from '../../../test-utils';
|
||||
import ShareLatestLocation from "../../../../src/components/views/beacon/ShareLatestLocation";
|
||||
import { copyPlaintext } from "../../../../src/utils/strings";
|
||||
import { flushPromises } from "../../../test-utils";
|
||||
|
||||
jest.mock('../../../../src/utils/strings', () => ({
|
||||
jest.mock("../../../../src/utils/strings", () => ({
|
||||
copyPlaintext: jest.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
describe('<ShareLatestLocation />', () => {
|
||||
describe("<ShareLatestLocation />", () => {
|
||||
const defaultProps = {
|
||||
latestLocationState: {
|
||||
uri: 'geo:51,42;u=35',
|
||||
uri: "geo:51,42;u=35",
|
||||
timestamp: 123,
|
||||
},
|
||||
};
|
||||
@@ -38,18 +38,18 @@ describe('<ShareLatestLocation />', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders null when no location', () => {
|
||||
it("renders null when no location", () => {
|
||||
const { container } = getComponent({ latestLocationState: undefined });
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
it('renders share buttons when there is a location', async () => {
|
||||
it("renders share buttons when there is a location", async () => {
|
||||
const { container, asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(container.querySelector('.mx_CopyableText_copyButton'));
|
||||
fireEvent.click(container.querySelector(".mx_CopyableText_copyButton"));
|
||||
await flushPromises();
|
||||
|
||||
expect(copyPlaintext).toHaveBeenCalledWith('51,42');
|
||||
expect(copyPlaintext).toHaveBeenCalledWith("51,42");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
|
||||
import StyledLiveBeaconIcon from '../../../../src/components/views/beacon/StyledLiveBeaconIcon';
|
||||
import StyledLiveBeaconIcon from "../../../../src/components/views/beacon/StyledLiveBeaconIcon";
|
||||
|
||||
describe('<StyledLiveBeaconIcon />', () => {
|
||||
describe("<StyledLiveBeaconIcon />", () => {
|
||||
const defaultProps = {};
|
||||
const getComponent = (props = {}) => render(<StyledLiveBeaconIcon {...defaultProps} {...props} />);
|
||||
|
||||
it('renders', () => {
|
||||
it("renders", () => {
|
||||
const { asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
jest.mock("../../../../src/utils/Feedback");
|
||||
jest.mock("../../../../src/settings/SettingsStore");
|
||||
|
||||
describe('<BetaCard />', () => {
|
||||
describe("<BetaCard />", () => {
|
||||
describe("Feedback prompt", () => {
|
||||
const featureId = "featureId";
|
||||
|
||||
|
||||
@@ -27,10 +27,13 @@ describe("<ContextMenu />", () => {
|
||||
// Hardcode window and menu dimensions
|
||||
const windowSize = 300;
|
||||
const menuSize = 200;
|
||||
jest.spyOn(UIStore, "instance", "get").mockImplementation(() => ({
|
||||
windowWidth: windowSize,
|
||||
windowHeight: windowSize,
|
||||
}) as unknown as UIStore);
|
||||
jest.spyOn(UIStore, "instance", "get").mockImplementation(
|
||||
() =>
|
||||
({
|
||||
windowWidth: windowSize,
|
||||
windowHeight: windowSize,
|
||||
} as unknown as UIStore),
|
||||
);
|
||||
window.Element.prototype.getBoundingClientRect = jest.fn().mockReturnValue({
|
||||
width: menuSize,
|
||||
height: menuSize,
|
||||
|
||||
@@ -14,33 +14,33 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount, ReactWrapper } from 'enzyme';
|
||||
import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||
import { mount, ReactWrapper } from "enzyme";
|
||||
import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import {
|
||||
PendingEventOrdering,
|
||||
BeaconIdentifier,
|
||||
Beacon,
|
||||
getBeaconInfoIdentifier,
|
||||
EventType,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
import { ExtensibleEvent, MessageEvent, M_POLL_KIND_DISCLOSED, PollStartEvent } from 'matrix-events-sdk';
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { ExtensibleEvent, MessageEvent, M_POLL_KIND_DISCLOSED, PollStartEvent } from "matrix-events-sdk";
|
||||
import { FeatureSupport, Thread } from "matrix-js-sdk/src/models/thread";
|
||||
import { mocked } from "jest-mock";
|
||||
import { act } from '@testing-library/react';
|
||||
import { act } from "@testing-library/react";
|
||||
|
||||
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext";
|
||||
import { IRoomState } from "../../../../src/components/structures/RoomView";
|
||||
import { canEditContent } from "../../../../src/utils/EventUtils";
|
||||
import { copyPlaintext, getSelectedText } from "../../../../src/utils/strings";
|
||||
import MessageContextMenu from "../../../../src/components/views/context_menus/MessageContextMenu";
|
||||
import { makeBeaconEvent, makeBeaconInfoEvent, makeLocationEvent, stubClient } from '../../../test-utils';
|
||||
import dispatcher from '../../../../src/dispatcher/dispatcher';
|
||||
import SettingsStore from '../../../../src/settings/SettingsStore';
|
||||
import { ReadPinsEventId } from '../../../../src/components/views/right_panel/types';
|
||||
import { makeBeaconEvent, makeBeaconInfoEvent, makeLocationEvent, stubClient } from "../../../test-utils";
|
||||
import dispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { ReadPinsEventId } from "../../../../src/components/views/right_panel/types";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
|
||||
jest.mock("../../../../src/utils/strings", () => ({
|
||||
@@ -52,17 +52,17 @@ jest.mock("../../../../src/utils/EventUtils", () => ({
|
||||
...jest.requireActual("../../../../src/utils/EventUtils"),
|
||||
canEditContent: jest.fn(),
|
||||
}));
|
||||
jest.mock('../../../../src/dispatcher/dispatcher');
|
||||
jest.mock("../../../../src/dispatcher/dispatcher");
|
||||
|
||||
const roomId = 'roomid';
|
||||
const roomId = "roomid";
|
||||
|
||||
describe('MessageContextMenu', () => {
|
||||
describe("MessageContextMenu", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
stubClient();
|
||||
});
|
||||
|
||||
it('does show copy link button when supplied a link', () => {
|
||||
it("does show copy link button when supplied a link", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const props = {
|
||||
link: "https://google.com/",
|
||||
@@ -73,119 +73,123 @@ describe('MessageContextMenu', () => {
|
||||
expect(copyLinkButton.props().href).toBe(props.link);
|
||||
});
|
||||
|
||||
it('does not show copy link button when not supplied a link', () => {
|
||||
it("does not show copy link button when not supplied a link", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const menu = createMenuWithContent(eventContent);
|
||||
const copyLinkButton = menu.find('a[aria-label="Copy link"]');
|
||||
expect(copyLinkButton).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe('message pinning', () => {
|
||||
describe("message pinning", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(true);
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockRestore();
|
||||
jest.spyOn(SettingsStore, "getValue").mockRestore();
|
||||
});
|
||||
|
||||
it('does not show pin option when user does not have rights to pin', () => {
|
||||
it("does not show pin option when user does not have rights to pin", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const event = new MatrixEvent(eventContent.serialize());
|
||||
|
||||
const room = makeDefaultRoom();
|
||||
// mock permission to disallow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, 'mayClientSendStateEvent').mockReturnValue(false);
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(false);
|
||||
|
||||
const menu = createMenu(event, {}, {}, undefined, room);
|
||||
|
||||
expect(menu.find('div[aria-label="Pin"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('does not show pin option for beacon_info event', () => {
|
||||
const deadBeaconEvent = makeBeaconInfoEvent('@alice:server.org', roomId, { isLive: false });
|
||||
it("does not show pin option for beacon_info event", () => {
|
||||
const deadBeaconEvent = makeBeaconInfoEvent("@alice:server.org", roomId, { isLive: false });
|
||||
|
||||
const room = makeDefaultRoom();
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, 'mayClientSendStateEvent').mockReturnValue(true);
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
|
||||
const menu = createMenu(deadBeaconEvent, {}, {}, undefined, room);
|
||||
|
||||
expect(menu.find('div[aria-label="Pin"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('does not show pin option when pinning feature is disabled', () => {
|
||||
it("does not show pin option when pinning feature is disabled", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const pinnableEvent = new MatrixEvent({ ...eventContent.serialize(), room_id: roomId });
|
||||
|
||||
const room = makeDefaultRoom();
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, 'mayClientSendStateEvent').mockReturnValue(true);
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
// disable pinning feature
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
|
||||
const menu = createMenu(pinnableEvent, {}, {}, undefined, room);
|
||||
|
||||
expect(menu.find('div[aria-label="Pin"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows pin option when pinning feature is enabled', () => {
|
||||
it("shows pin option when pinning feature is enabled", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const pinnableEvent = new MatrixEvent({ ...eventContent.serialize(), room_id: roomId });
|
||||
|
||||
const room = makeDefaultRoom();
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, 'mayClientSendStateEvent').mockReturnValue(true);
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
|
||||
const menu = createMenu(pinnableEvent, {}, {}, undefined, room);
|
||||
|
||||
expect(menu.find('div[aria-label="Pin"]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('pins event on pin option click', () => {
|
||||
it("pins event on pin option click", () => {
|
||||
const onFinished = jest.fn();
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const pinnableEvent = new MatrixEvent({ ...eventContent.serialize(), room_id: roomId });
|
||||
pinnableEvent.event.event_id = '!3';
|
||||
pinnableEvent.event.event_id = "!3";
|
||||
const client = MatrixClientPeg.get();
|
||||
const room = makeDefaultRoom();
|
||||
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, 'mayClientSendStateEvent').mockReturnValue(true);
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
|
||||
// mock read pins account data
|
||||
const pinsAccountData = new MatrixEvent({ content: { event_ids: ['!1', '!2'] } });
|
||||
jest.spyOn(room, 'getAccountData').mockReturnValue(pinsAccountData);
|
||||
const pinsAccountData = new MatrixEvent({ content: { event_ids: ["!1", "!2"] } });
|
||||
jest.spyOn(room, "getAccountData").mockReturnValue(pinsAccountData);
|
||||
|
||||
const menu = createMenu(pinnableEvent, { onFinished }, {}, undefined, room);
|
||||
|
||||
act(() => {
|
||||
menu.find('div[aria-label="Pin"]').simulate('click');
|
||||
menu.find('div[aria-label="Pin"]').simulate("click");
|
||||
});
|
||||
|
||||
// added to account data
|
||||
expect(client.setRoomAccountData).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
ReadPinsEventId,
|
||||
{ event_ids: [
|
||||
expect(client.setRoomAccountData).toHaveBeenCalledWith(roomId, ReadPinsEventId, {
|
||||
event_ids: [
|
||||
// from account data
|
||||
'!1', '!2',
|
||||
"!1",
|
||||
"!2",
|
||||
pinnableEvent.getId(),
|
||||
],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// add to room's pins
|
||||
expect(client.sendStateEvent).toHaveBeenCalledWith(roomId, EventType.RoomPinnedEvents, {
|
||||
pinned: [pinnableEvent.getId()] }, "");
|
||||
expect(client.sendStateEvent).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
EventType.RoomPinnedEvents,
|
||||
{
|
||||
pinned: [pinnableEvent.getId()],
|
||||
},
|
||||
"",
|
||||
);
|
||||
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('unpins event on pin option click when event is pinned', () => {
|
||||
it("unpins event on pin option click when event is pinned", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const pinnableEvent = new MatrixEvent({ ...eventContent.serialize(), room_id: roomId });
|
||||
pinnableEvent.event.event_id = '!3';
|
||||
pinnableEvent.event.event_id = "!3";
|
||||
const client = MatrixClientPeg.get();
|
||||
const room = makeDefaultRoom();
|
||||
|
||||
@@ -194,52 +198,53 @@ describe('MessageContextMenu', () => {
|
||||
type: EventType.RoomPinnedEvents,
|
||||
room_id: roomId,
|
||||
state_key: "",
|
||||
content: { pinned: [pinnableEvent.getId(), '!another-event'] },
|
||||
content: { pinned: [pinnableEvent.getId(), "!another-event"] },
|
||||
});
|
||||
room.currentState.setStateEvents([pinEvent]);
|
||||
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, 'mayClientSendStateEvent').mockReturnValue(true);
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
|
||||
// mock read pins account data
|
||||
const pinsAccountData = new MatrixEvent({ content: { event_ids: ['!1', '!2'] } });
|
||||
jest.spyOn(room, 'getAccountData').mockReturnValue(pinsAccountData);
|
||||
const pinsAccountData = new MatrixEvent({ content: { event_ids: ["!1", "!2"] } });
|
||||
jest.spyOn(room, "getAccountData").mockReturnValue(pinsAccountData);
|
||||
|
||||
const menu = createMenu(pinnableEvent, {}, {}, undefined, room);
|
||||
|
||||
act(() => {
|
||||
menu.find('div[aria-label="Unpin"]').simulate('click');
|
||||
menu.find('div[aria-label="Unpin"]').simulate("click");
|
||||
});
|
||||
|
||||
expect(client.setRoomAccountData).not.toHaveBeenCalled();
|
||||
|
||||
// add to room's pins
|
||||
expect(client.sendStateEvent).toHaveBeenCalledWith(
|
||||
roomId, EventType.RoomPinnedEvents,
|
||||
roomId,
|
||||
EventType.RoomPinnedEvents,
|
||||
// pinnableEvent's id removed, other pins intact
|
||||
{ pinned: ['!another-event'] },
|
||||
{ pinned: ["!another-event"] },
|
||||
"",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('message forwarding', () => {
|
||||
it('allows forwarding a room message', () => {
|
||||
describe("message forwarding", () => {
|
||||
it("allows forwarding a room message", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const menu = createMenuWithContent(eventContent);
|
||||
expect(menu.find('div[aria-label="Forward"]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('does not allow forwarding a poll', () => {
|
||||
it("does not allow forwarding a poll", () => {
|
||||
const eventContent = PollStartEvent.from("why?", ["42"], M_POLL_KIND_DISCLOSED);
|
||||
const menu = createMenuWithContent(eventContent);
|
||||
expect(menu.find('div[aria-label="Forward"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe('forwarding beacons', () => {
|
||||
describe("forwarding beacons", () => {
|
||||
const aliceId = "@alice:server.org";
|
||||
|
||||
it('does not allow forwarding a beacon that is not live', () => {
|
||||
it("does not allow forwarding a beacon that is not live", () => {
|
||||
const deadBeaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: false });
|
||||
const beacon = new Beacon(deadBeaconEvent);
|
||||
const beacons = new Map<BeaconIdentifier, Beacon>();
|
||||
@@ -248,11 +253,12 @@ describe('MessageContextMenu', () => {
|
||||
expect(menu.find('div[aria-label="Forward"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('does not allow forwarding a beacon that is not live but has a latestLocation', () => {
|
||||
it("does not allow forwarding a beacon that is not live but has a latestLocation", () => {
|
||||
const deadBeaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: false });
|
||||
const beaconLocation = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: deadBeaconEvent.getId(), geoUri: 'geo:51,41' },
|
||||
);
|
||||
const beaconLocation = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: deadBeaconEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
});
|
||||
const beacon = new Beacon(deadBeaconEvent);
|
||||
// @ts-ignore illegally set private prop
|
||||
beacon._latestLocationEvent = beaconLocation;
|
||||
@@ -262,7 +268,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(menu.find('div[aria-label="Forward"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('does not allow forwarding a live beacon that does not have a latestLocation', () => {
|
||||
it("does not allow forwarding a live beacon that does not have a latestLocation", () => {
|
||||
const beaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true });
|
||||
|
||||
const beacon = new Beacon(beaconEvent);
|
||||
@@ -272,11 +278,12 @@ describe('MessageContextMenu', () => {
|
||||
expect(menu.find('div[aria-label="Forward"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('allows forwarding a live beacon that has a location', () => {
|
||||
it("allows forwarding a live beacon that has a location", () => {
|
||||
const liveBeaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true });
|
||||
const beaconLocation = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: liveBeaconEvent.getId(), geoUri: 'geo:51,41' },
|
||||
);
|
||||
const beaconLocation = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: liveBeaconEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
});
|
||||
const beacon = new Beacon(liveBeaconEvent);
|
||||
// @ts-ignore illegally set private prop
|
||||
beacon._latestLocationEvent = beaconLocation;
|
||||
@@ -286,12 +293,13 @@ describe('MessageContextMenu', () => {
|
||||
expect(menu.find('div[aria-label="Forward"]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('opens forward dialog with correct event', () => {
|
||||
const dispatchSpy = jest.spyOn(dispatcher, 'dispatch');
|
||||
it("opens forward dialog with correct event", () => {
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
const liveBeaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true });
|
||||
const beaconLocation = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: liveBeaconEvent.getId(), geoUri: 'geo:51,41' },
|
||||
);
|
||||
const beaconLocation = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: liveBeaconEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
});
|
||||
const beacon = new Beacon(liveBeaconEvent);
|
||||
// @ts-ignore illegally set private prop
|
||||
beacon._latestLocationEvent = beaconLocation;
|
||||
@@ -300,26 +308,28 @@ describe('MessageContextMenu', () => {
|
||||
const menu = createMenu(liveBeaconEvent, {}, {}, beacons);
|
||||
|
||||
act(() => {
|
||||
menu.find('div[aria-label="Forward"]').simulate('click');
|
||||
menu.find('div[aria-label="Forward"]').simulate("click");
|
||||
});
|
||||
|
||||
// called with forwardableEvent, not beaconInfo event
|
||||
expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({
|
||||
event: beaconLocation,
|
||||
}));
|
||||
expect(dispatchSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
event: beaconLocation,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('open as map link', () => {
|
||||
it('does not allow opening a plain message in open street maps', () => {
|
||||
describe("open as map link", () => {
|
||||
it("does not allow opening a plain message in open street maps", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const menu = createMenuWithContent(eventContent);
|
||||
expect(menu.find('a[aria-label="Open in OpenStreetMap"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('does not allow opening a beacon that does not have a shareable location event', () => {
|
||||
const deadBeaconEvent = makeBeaconInfoEvent('@alice', roomId, { isLive: false });
|
||||
it("does not allow opening a beacon that does not have a shareable location event", () => {
|
||||
const deadBeaconEvent = makeBeaconInfoEvent("@alice", roomId, { isLive: false });
|
||||
const beacon = new Beacon(deadBeaconEvent);
|
||||
const beacons = new Map<BeaconIdentifier, Beacon>();
|
||||
beacons.set(getBeaconInfoIdentifier(deadBeaconEvent), beacon);
|
||||
@@ -327,20 +337,21 @@ describe('MessageContextMenu', () => {
|
||||
expect(menu.find('a[aria-label="Open in OpenStreetMap"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('allows opening a location event in open street map', () => {
|
||||
const locationEvent = makeLocationEvent('geo:50,50');
|
||||
it("allows opening a location event in open street map", () => {
|
||||
const locationEvent = makeLocationEvent("geo:50,50");
|
||||
const menu = createMenu(locationEvent);
|
||||
// exists with a href with the lat/lon from the location event
|
||||
expect(
|
||||
menu.find('a[aria-label="Open in OpenStreetMap"]').at(0).props().href,
|
||||
).toEqual('https://www.openstreetmap.org/?mlat=50&mlon=50#map=16/50/50');
|
||||
expect(menu.find('a[aria-label="Open in OpenStreetMap"]').at(0).props().href).toEqual(
|
||||
"https://www.openstreetmap.org/?mlat=50&mlon=50#map=16/50/50",
|
||||
);
|
||||
});
|
||||
|
||||
it('allows opening a beacon that has a shareable location event', () => {
|
||||
const liveBeaconEvent = makeBeaconInfoEvent('@alice', roomId, { isLive: true });
|
||||
const beaconLocation = makeBeaconEvent(
|
||||
'@alice', { beaconInfoId: liveBeaconEvent.getId(), geoUri: 'geo:51,41' },
|
||||
);
|
||||
it("allows opening a beacon that has a shareable location event", () => {
|
||||
const liveBeaconEvent = makeBeaconInfoEvent("@alice", roomId, { isLive: true });
|
||||
const beaconLocation = makeBeaconEvent("@alice", {
|
||||
beaconInfoId: liveBeaconEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
});
|
||||
const beacon = new Beacon(liveBeaconEvent);
|
||||
// @ts-ignore illegally set private prop
|
||||
beacon._latestLocationEvent = beaconLocation;
|
||||
@@ -348,14 +359,14 @@ describe('MessageContextMenu', () => {
|
||||
beacons.set(getBeaconInfoIdentifier(liveBeaconEvent), beacon);
|
||||
const menu = createMenu(liveBeaconEvent, {}, {}, beacons);
|
||||
// exists with a href with the lat/lon from the location event
|
||||
expect(
|
||||
menu.find('a[aria-label="Open in OpenStreetMap"]').at(0).props().href,
|
||||
).toEqual('https://www.openstreetmap.org/?mlat=51&mlon=41#map=16/51/41');
|
||||
expect(menu.find('a[aria-label="Open in OpenStreetMap"]').at(0).props().href).toEqual(
|
||||
"https://www.openstreetmap.org/?mlat=51&mlon=41#map=16/51/41",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("right click", () => {
|
||||
it('copy button does work as expected', () => {
|
||||
it("copy button does work as expected", () => {
|
||||
const text = "hello";
|
||||
const eventContent = MessageEvent.from(text);
|
||||
mocked(getSelectedText).mockReturnValue(text);
|
||||
@@ -366,7 +377,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(copyPlaintext).toHaveBeenCalledWith(text);
|
||||
});
|
||||
|
||||
it('copy button is not shown when there is nothing to copy', () => {
|
||||
it("copy button is not shown when there is nothing to copy", () => {
|
||||
const text = "hello";
|
||||
const eventContent = MessageEvent.from(text);
|
||||
mocked(getSelectedText).mockReturnValue("");
|
||||
@@ -376,7 +387,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(copyButton).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows edit button when we can edit', () => {
|
||||
it("shows edit button when we can edit", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
mocked(canEditContent).mockReturnValue(true);
|
||||
|
||||
@@ -385,7 +396,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(editButton).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('does not show edit button when we cannot edit', () => {
|
||||
it("does not show edit button when we cannot edit", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
mocked(canEditContent).mockReturnValue(false);
|
||||
|
||||
@@ -394,7 +405,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(editButton).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows reply button when we can reply', () => {
|
||||
it("shows reply button when we can reply", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const context = {
|
||||
canSendMessages: true,
|
||||
@@ -405,7 +416,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(replyButton).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('does not show reply button when we cannot reply', () => {
|
||||
it("does not show reply button when we cannot reply", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const context = {
|
||||
canSendMessages: true,
|
||||
@@ -419,7 +430,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(replyButton).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows react button when we can react', () => {
|
||||
it("shows react button when we can react", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const context = {
|
||||
canReact: true,
|
||||
@@ -430,7 +441,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(reactButton).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('does not show react button when we cannot react', () => {
|
||||
it("does not show react button when we cannot react", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const context = {
|
||||
canReact: false,
|
||||
@@ -441,10 +452,10 @@ describe('MessageContextMenu', () => {
|
||||
expect(reactButton).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows view in room button when the event is a thread root', () => {
|
||||
it("shows view in room button when the event is a thread root", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const mxEvent = new MatrixEvent(eventContent.serialize());
|
||||
mxEvent.getThread = () => ({ rootEvent: mxEvent }) as Thread;
|
||||
mxEvent.getThread = () => ({ rootEvent: mxEvent } as Thread);
|
||||
const props = {
|
||||
rightClick: true,
|
||||
};
|
||||
@@ -457,7 +468,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(reactButton).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('does not show view in room button when the event is not a thread root', () => {
|
||||
it("does not show view in room button when the event is not a thread root", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
|
||||
const menu = createRightClickMenuWithContent(eventContent);
|
||||
@@ -465,7 +476,7 @@ describe('MessageContextMenu', () => {
|
||||
expect(reactButton).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('creates a new thread on reply in thread click', () => {
|
||||
it("creates a new thread on reply in thread click", () => {
|
||||
const eventContent = MessageEvent.from("hello");
|
||||
const mxEvent = new MatrixEvent(eventContent.serialize());
|
||||
|
||||
@@ -473,7 +484,7 @@ describe('MessageContextMenu', () => {
|
||||
const context = {
|
||||
canSendMessages: true,
|
||||
};
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(true);
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
|
||||
const menu = createRightClickMenu(mxEvent, context);
|
||||
|
||||
@@ -490,10 +501,7 @@ describe('MessageContextMenu', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function createRightClickMenuWithContent(
|
||||
eventContent: ExtensibleEvent,
|
||||
context?: Partial<IRoomState>,
|
||||
): ReactWrapper {
|
||||
function createRightClickMenuWithContent(eventContent: ExtensibleEvent, context?: Partial<IRoomState>): ReactWrapper {
|
||||
return createMenuWithContent(eventContent, { rightClick: true }, context);
|
||||
}
|
||||
|
||||
@@ -511,14 +519,9 @@ function createMenuWithContent(
|
||||
}
|
||||
|
||||
function makeDefaultRoom(): Room {
|
||||
return new Room(
|
||||
roomId,
|
||||
MatrixClientPeg.get(),
|
||||
"@user:example.com",
|
||||
{
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
},
|
||||
);
|
||||
return new Room(roomId, MatrixClientPeg.get(), "@user:example.com", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
}
|
||||
|
||||
function createMenu(
|
||||
@@ -540,12 +543,7 @@ function createMenu(
|
||||
|
||||
return mount(
|
||||
<RoomContext.Provider value={context as IRoomState}>
|
||||
<MessageContextMenu
|
||||
chevronFace={null}
|
||||
mxEvent={mxEvent}
|
||||
onFinished={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
<MessageContextMenu chevronFace={null} mxEvent={mxEvent} onFinished={jest.fn()} {...props} />
|
||||
</RoomContext.Provider>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,33 +14,33 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { Room } from 'matrix-js-sdk/src/matrix';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import 'focus-visible'; // to fix context menus
|
||||
import { mount } from "enzyme";
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import "focus-visible"; // to fix context menus
|
||||
|
||||
import SpaceContextMenu from '../../../../src/components/views/context_menus/SpaceContextMenu';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import { findByTestId } from '../../../test-utils';
|
||||
import SpaceContextMenu from "../../../../src/components/views/context_menus/SpaceContextMenu";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import { findByTestId } from "../../../test-utils";
|
||||
import {
|
||||
shouldShowSpaceSettings,
|
||||
showCreateNewRoom,
|
||||
showCreateNewSubspace,
|
||||
showSpaceInvite,
|
||||
showSpaceSettings,
|
||||
} from '../../../../src/utils/space';
|
||||
} from "../../../../src/utils/space";
|
||||
import { leaveSpace } from "../../../../src/utils/leave-behaviour";
|
||||
import { shouldShowComponent } from '../../../../src/customisations/helpers/UIComponents';
|
||||
import { UIComponent } from '../../../../src/settings/UIFeature';
|
||||
import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../src/settings/UIFeature";
|
||||
|
||||
jest.mock('../../../../src/customisations/helpers/UIComponents', () => ({
|
||||
jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../src/utils/space', () => ({
|
||||
jest.mock("../../../../src/utils/space", () => ({
|
||||
shouldShowSpaceSettings: jest.fn(),
|
||||
showCreateNewRoom: jest.fn(),
|
||||
showCreateNewSubspace: jest.fn(),
|
||||
@@ -49,172 +49,172 @@ jest.mock('../../../../src/utils/space', () => ({
|
||||
showSpaceSettings: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../src/utils/leave-behaviour', () => ({
|
||||
jest.mock("../../../../src/utils/leave-behaviour", () => ({
|
||||
leaveSpace: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('<SpaceContextMenu />', () => {
|
||||
const userId = '@test:server';
|
||||
describe("<SpaceContextMenu />", () => {
|
||||
const userId = "@test:server";
|
||||
const mockClient = {
|
||||
getUserId: jest.fn().mockReturnValue(userId),
|
||||
};
|
||||
const makeMockSpace = (props = {}) => ({
|
||||
name: 'test space',
|
||||
getJoinRule: jest.fn(),
|
||||
canInvite: jest.fn(),
|
||||
currentState: {
|
||||
maySendStateEvent: jest.fn(),
|
||||
},
|
||||
client: mockClient,
|
||||
getMyMembership: jest.fn(),
|
||||
...props,
|
||||
}) as unknown as Room;
|
||||
const makeMockSpace = (props = {}) =>
|
||||
({
|
||||
name: "test space",
|
||||
getJoinRule: jest.fn(),
|
||||
canInvite: jest.fn(),
|
||||
currentState: {
|
||||
maySendStateEvent: jest.fn(),
|
||||
},
|
||||
client: mockClient,
|
||||
getMyMembership: jest.fn(),
|
||||
...props,
|
||||
} as unknown as Room);
|
||||
const defaultProps = {
|
||||
space: makeMockSpace(),
|
||||
onFinished: jest.fn(),
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<SpaceContextMenu {...defaultProps} {...props} />,
|
||||
{
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: {
|
||||
value: mockClient,
|
||||
},
|
||||
});
|
||||
mount(<SpaceContextMenu {...defaultProps} {...props} />, {
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: {
|
||||
value: mockClient,
|
||||
},
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
mockClient.getUserId.mockReturnValue(userId);
|
||||
});
|
||||
|
||||
it('renders menu correctly', () => {
|
||||
it("renders menu correctly", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders invite option when space is public', () => {
|
||||
it("renders invite option when space is public", () => {
|
||||
const space = makeMockSpace({
|
||||
getJoinRule: jest.fn().mockReturnValue('public'),
|
||||
getJoinRule: jest.fn().mockReturnValue("public"),
|
||||
});
|
||||
const component = getComponent({ space });
|
||||
expect(findByTestId(component, 'invite-option').length).toBeTruthy();
|
||||
expect(findByTestId(component, "invite-option").length).toBeTruthy();
|
||||
});
|
||||
it('renders invite option when user is has invite rights for space', () => {
|
||||
it("renders invite option when user is has invite rights for space", () => {
|
||||
const space = makeMockSpace({
|
||||
canInvite: jest.fn().mockReturnValue(true),
|
||||
});
|
||||
const component = getComponent({ space });
|
||||
expect(space.canInvite).toHaveBeenCalledWith(userId);
|
||||
expect(findByTestId(component, 'invite-option').length).toBeTruthy();
|
||||
expect(findByTestId(component, "invite-option").length).toBeTruthy();
|
||||
});
|
||||
it('opens invite dialog when invite option is clicked', () => {
|
||||
it("opens invite dialog when invite option is clicked", () => {
|
||||
const space = makeMockSpace({
|
||||
getJoinRule: jest.fn().mockReturnValue('public'),
|
||||
getJoinRule: jest.fn().mockReturnValue("public"),
|
||||
});
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ space, onFinished });
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'invite-option').at(0).simulate('click');
|
||||
findByTestId(component, "invite-option").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(showSpaceInvite).toHaveBeenCalledWith(space);
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
it('renders space settings option when user has rights', () => {
|
||||
it("renders space settings option when user has rights", () => {
|
||||
mocked(shouldShowSpaceSettings).mockReturnValue(true);
|
||||
const component = getComponent();
|
||||
expect(shouldShowSpaceSettings).toHaveBeenCalledWith(defaultProps.space);
|
||||
expect(findByTestId(component, 'settings-option').length).toBeTruthy();
|
||||
expect(findByTestId(component, "settings-option").length).toBeTruthy();
|
||||
});
|
||||
it('opens space settings when space settings option is clicked', () => {
|
||||
it("opens space settings when space settings option is clicked", () => {
|
||||
mocked(shouldShowSpaceSettings).mockReturnValue(true);
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'settings-option').at(0).simulate('click');
|
||||
findByTestId(component, "settings-option").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(showSpaceSettings).toHaveBeenCalledWith(defaultProps.space);
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
it('renders leave option when user does not have rights to see space settings', () => {
|
||||
it("renders leave option when user does not have rights to see space settings", () => {
|
||||
const component = getComponent();
|
||||
expect(findByTestId(component, 'leave-option').length).toBeTruthy();
|
||||
expect(findByTestId(component, "leave-option").length).toBeTruthy();
|
||||
});
|
||||
it('leaves space when leave option is clicked', () => {
|
||||
it("leaves space when leave option is clicked", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
act(() => {
|
||||
findByTestId(component, 'leave-option').at(0).simulate('click');
|
||||
findByTestId(component, "leave-option").at(0).simulate("click");
|
||||
});
|
||||
expect(leaveSpace).toHaveBeenCalledWith(defaultProps.space);
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
describe('add children section', () => {
|
||||
describe("add children section", () => {
|
||||
const space = makeMockSpace();
|
||||
beforeEach(() => {
|
||||
// set space to allow adding children to space
|
||||
mocked(space.currentState.maySendStateEvent).mockReturnValue(true);
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
});
|
||||
it('does not render section when user does not have permission to add children', () => {
|
||||
it("does not render section when user does not have permission to add children", () => {
|
||||
mocked(space.currentState.maySendStateEvent).mockReturnValue(false);
|
||||
const component = getComponent({ space });
|
||||
|
||||
expect(findByTestId(component, 'add-to-space-header').length).toBeFalsy();
|
||||
expect(findByTestId(component, 'new-room-option').length).toBeFalsy();
|
||||
expect(findByTestId(component, 'new-subspace-option').length).toBeFalsy();
|
||||
expect(findByTestId(component, "add-to-space-header").length).toBeFalsy();
|
||||
expect(findByTestId(component, "new-room-option").length).toBeFalsy();
|
||||
expect(findByTestId(component, "new-subspace-option").length).toBeFalsy();
|
||||
});
|
||||
it('does not render section when UIComponent customisations disable room and space creation', () => {
|
||||
it("does not render section when UIComponent customisations disable room and space creation", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(false);
|
||||
const component = getComponent({ space });
|
||||
|
||||
expect(shouldShowComponent).toHaveBeenCalledWith(UIComponent.CreateRooms);
|
||||
expect(shouldShowComponent).toHaveBeenCalledWith(UIComponent.CreateSpaces);
|
||||
|
||||
expect(findByTestId(component, 'add-to-space-header').length).toBeFalsy();
|
||||
expect(findByTestId(component, 'new-room-option').length).toBeFalsy();
|
||||
expect(findByTestId(component, 'new-subspace-option').length).toBeFalsy();
|
||||
expect(findByTestId(component, "add-to-space-header").length).toBeFalsy();
|
||||
expect(findByTestId(component, "new-room-option").length).toBeFalsy();
|
||||
expect(findByTestId(component, "new-subspace-option").length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('renders section with add room button when UIComponent customisation allows CreateRoom', () => {
|
||||
it("renders section with add room button when UIComponent customisation allows CreateRoom", () => {
|
||||
// only allow CreateRoom
|
||||
mocked(shouldShowComponent).mockImplementation(feature => feature === UIComponent.CreateRooms);
|
||||
mocked(shouldShowComponent).mockImplementation((feature) => feature === UIComponent.CreateRooms);
|
||||
const component = getComponent({ space });
|
||||
|
||||
expect(findByTestId(component, 'add-to-space-header').length).toBeTruthy();
|
||||
expect(findByTestId(component, 'new-room-option').length).toBeTruthy();
|
||||
expect(findByTestId(component, 'new-subspace-option').length).toBeFalsy();
|
||||
expect(findByTestId(component, "add-to-space-header").length).toBeTruthy();
|
||||
expect(findByTestId(component, "new-room-option").length).toBeTruthy();
|
||||
expect(findByTestId(component, "new-subspace-option").length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('renders section with add space button when UIComponent customisation allows CreateSpace', () => {
|
||||
it("renders section with add space button when UIComponent customisation allows CreateSpace", () => {
|
||||
// only allow CreateSpaces
|
||||
mocked(shouldShowComponent).mockImplementation(feature => feature === UIComponent.CreateSpaces);
|
||||
mocked(shouldShowComponent).mockImplementation((feature) => feature === UIComponent.CreateSpaces);
|
||||
const component = getComponent({ space });
|
||||
|
||||
expect(findByTestId(component, 'add-to-space-header').length).toBeTruthy();
|
||||
expect(findByTestId(component, 'new-room-option').length).toBeFalsy();
|
||||
expect(findByTestId(component, 'new-subspace-option').length).toBeTruthy();
|
||||
expect(findByTestId(component, "add-to-space-header").length).toBeTruthy();
|
||||
expect(findByTestId(component, "new-room-option").length).toBeFalsy();
|
||||
expect(findByTestId(component, "new-subspace-option").length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('opens create room dialog on add room button click', () => {
|
||||
it("opens create room dialog on add room button click", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ space, onFinished });
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'new-room-option').at(0).simulate('click');
|
||||
findByTestId(component, "new-room-option").at(0).simulate("click");
|
||||
});
|
||||
expect(showCreateNewRoom).toHaveBeenCalledWith(space);
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
it('opens create space dialog on add space button click', () => {
|
||||
it("opens create space dialog on add space button click", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ space, onFinished });
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'new-subspace-option').at(0).simulate('click');
|
||||
findByTestId(component, "new-subspace-option").at(0).simulate("click");
|
||||
});
|
||||
expect(showCreateNewSubspace).toHaveBeenCalledWith(space);
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
|
||||
@@ -38,10 +38,7 @@ describe("ThreadListContextMenu", () => {
|
||||
let event: MatrixEvent;
|
||||
|
||||
function getComponent(props: Partial<ThreadListContextMenuProps>) {
|
||||
return render(<ThreadListContextMenu
|
||||
mxEvent={event}
|
||||
{...props}
|
||||
/>);
|
||||
return render(<ThreadListContextMenu mxEvent={event} {...props} />);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount, ReactWrapper } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { IPassphraseInfo } from 'matrix-js-sdk/src/crypto/api';
|
||||
import { mount, ReactWrapper } from "enzyme";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { IPassphraseInfo } from "matrix-js-sdk/src/crypto/api";
|
||||
|
||||
import { findByAttr, getMockClientWithEventEmitter, unmockClientPeg } from '../../../test-utils';
|
||||
import { findById, flushPromises } from '../../../test-utils';
|
||||
import { findByAttr, getMockClientWithEventEmitter, unmockClientPeg } from "../../../test-utils";
|
||||
import { findById, flushPromises } from "../../../test-utils";
|
||||
import AccessSecretStorageDialog from "../../../../src/components/views/dialogs/security/AccessSecretStorageDialog";
|
||||
|
||||
describe("AccessSecretStorageDialog", () => {
|
||||
@@ -35,12 +35,12 @@ describe("AccessSecretStorageDialog", () => {
|
||||
checkPrivateKey: jest.fn(),
|
||||
keyInfo: undefined,
|
||||
};
|
||||
const getComponent = (props ={}): ReactWrapper =>
|
||||
const getComponent = (props = {}): ReactWrapper =>
|
||||
mount(<AccessSecretStorageDialog {...defaultProps} {...props} />);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockClient.keyBackupKeyFromRecoveryKey.mockReturnValue('a raw key' as unknown as Uint8Array);
|
||||
mockClient.keyBackupKeyFromRecoveryKey.mockReturnValue("a raw key" as unknown as Uint8Array);
|
||||
mockClient.isValidRecoveryKey.mockReturnValue(false);
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ describe("AccessSecretStorageDialog", () => {
|
||||
const e = { preventDefault: () => {} };
|
||||
|
||||
act(() => {
|
||||
wrapper.find('form').simulate('submit', e);
|
||||
wrapper.find("form").simulate("submit", e);
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
@@ -75,13 +75,13 @@ describe("AccessSecretStorageDialog", () => {
|
||||
it("Considers a valid key to be valid", async () => {
|
||||
const checkPrivateKey = jest.fn().mockResolvedValue(true);
|
||||
const wrapper = getComponent({ checkPrivateKey });
|
||||
mockClient.keyBackupKeyFromRecoveryKey.mockReturnValue('a raw key' as unknown as Uint8Array);
|
||||
mockClient.keyBackupKeyFromRecoveryKey.mockReturnValue("a raw key" as unknown as Uint8Array);
|
||||
mockClient.checkSecretStorageKey.mockResolvedValue(true);
|
||||
|
||||
const v = "asdf";
|
||||
const e = { target: { value: v } };
|
||||
act(() => {
|
||||
findById(wrapper, 'mx_securityKey').find('input').simulate('change', e);
|
||||
findById(wrapper, "mx_securityKey").find("input").simulate("change", e);
|
||||
wrapper.setProps({});
|
||||
});
|
||||
await act(async () => {
|
||||
@@ -91,10 +91,10 @@ describe("AccessSecretStorageDialog", () => {
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
const submitButton = findByAttr('data-testid')(wrapper, 'dialog-primary-button').at(0);
|
||||
const submitButton = findByAttr("data-testid")(wrapper, "dialog-primary-button").at(0);
|
||||
// submit button is enabled when key is valid
|
||||
expect(submitButton.props().disabled).toBeFalsy();
|
||||
expect(wrapper.find('.mx_AccessSecretStorageDialog_recoveryKeyFeedback').text()).toEqual('Looks good!');
|
||||
expect(wrapper.find(".mx_AccessSecretStorageDialog_recoveryKeyFeedback").text()).toEqual("Looks good!");
|
||||
});
|
||||
|
||||
it("Notifies the user if they input an invalid Security Key", async () => {
|
||||
@@ -106,36 +106,36 @@ describe("AccessSecretStorageDialog", () => {
|
||||
});
|
||||
|
||||
act(() => {
|
||||
findById(wrapper, 'mx_securityKey').find('input').simulate('change', e);
|
||||
findById(wrapper, "mx_securityKey").find("input").simulate("change", e);
|
||||
});
|
||||
// force a validation now because it debounces
|
||||
// @ts-ignore private
|
||||
await wrapper.instance().validateRecoveryKey();
|
||||
|
||||
const submitButton = findByAttr('data-testid')(wrapper, 'dialog-primary-button').at(0);
|
||||
const submitButton = findByAttr("data-testid")(wrapper, "dialog-primary-button").at(0);
|
||||
// submit button is disabled when recovery key is invalid
|
||||
expect(submitButton.props().disabled).toBeTruthy();
|
||||
expect(
|
||||
wrapper.find('.mx_AccessSecretStorageDialog_recoveryKeyFeedback').text(),
|
||||
).toEqual('Invalid Security Key');
|
||||
expect(wrapper.find(".mx_AccessSecretStorageDialog_recoveryKeyFeedback").text()).toEqual(
|
||||
"Invalid Security Key",
|
||||
);
|
||||
|
||||
wrapper.setProps({});
|
||||
const notification = wrapper.find(".mx_AccessSecretStorageDialog_recoveryKeyFeedback");
|
||||
expect(notification.props().children).toEqual("Invalid Security Key");
|
||||
});
|
||||
|
||||
it("Notifies the user if they input an invalid passphrase", async function() {
|
||||
it("Notifies the user if they input an invalid passphrase", async function () {
|
||||
const keyInfo = {
|
||||
name: 'test',
|
||||
algorithm: 'test',
|
||||
iv: 'test',
|
||||
mac: '1:2:3:4',
|
||||
name: "test",
|
||||
algorithm: "test",
|
||||
iv: "test",
|
||||
mac: "1:2:3:4",
|
||||
passphrase: {
|
||||
// this type is weird in js-sdk
|
||||
// cast 'm.pbkdf2' to itself
|
||||
algorithm: 'm.pbkdf2' as IPassphraseInfo['algorithm'],
|
||||
algorithm: "m.pbkdf2" as IPassphraseInfo["algorithm"],
|
||||
iterations: 2,
|
||||
salt: 'nonempty',
|
||||
salt: "nonempty",
|
||||
},
|
||||
};
|
||||
const checkPrivateKey = jest.fn().mockResolvedValue(false);
|
||||
@@ -145,23 +145,24 @@ describe("AccessSecretStorageDialog", () => {
|
||||
// update passphrase
|
||||
act(() => {
|
||||
const e = { target: { value: "a" } };
|
||||
findById(wrapper, 'mx_passPhraseInput').at(1).simulate('change', e);
|
||||
findById(wrapper, "mx_passPhraseInput").at(1).simulate("change", e);
|
||||
});
|
||||
wrapper.setProps({});
|
||||
|
||||
// input updated
|
||||
expect(findById(wrapper, 'mx_passPhraseInput').at(0).props().value).toEqual('a');
|
||||
expect(findById(wrapper, "mx_passPhraseInput").at(0).props().value).toEqual("a");
|
||||
|
||||
// submit the form
|
||||
act(() => {
|
||||
wrapper.find('form').at(0).simulate('submit');
|
||||
wrapper.find("form").at(0).simulate("submit");
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
wrapper.setProps({});
|
||||
const notification = wrapper.find(".mx_AccessSecretStorageDialog_keyStatus");
|
||||
expect(notification.props().children).toEqual(
|
||||
["\uD83D\uDC4E ", "Unable to access secret storage. Please verify that you " +
|
||||
"entered the correct Security Phrase."]);
|
||||
expect(notification.props().children).toEqual([
|
||||
"\uD83D\uDC4E ",
|
||||
"Unable to access secret storage. Please verify that you " + "entered the correct Security Phrase.",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,11 +35,13 @@ describe("<ChangelogDialog />", () => {
|
||||
ahead_by: 24,
|
||||
behind_by: 0,
|
||||
total_commits: 24,
|
||||
commits: [{
|
||||
sha: "commit-sha",
|
||||
html_url: "https://api.github.com/repos/vector-im/element-web/commit/commit-sha",
|
||||
commit: { message: "This is the first commit message" },
|
||||
}],
|
||||
commits: [
|
||||
{
|
||||
sha: "commit-sha",
|
||||
html_url: "https://api.github.com/repos/vector-im/element-web/commit/commit-sha",
|
||||
commit: { message: "This is the first commit message" },
|
||||
},
|
||||
],
|
||||
files: [],
|
||||
});
|
||||
const reactUrl = "https://riot.im/github/repos/matrix-org/matrix-react-sdk/compare/oldsha2...newsha2";
|
||||
@@ -55,11 +57,13 @@ describe("<ChangelogDialog />", () => {
|
||||
ahead_by: 83,
|
||||
behind_by: 0,
|
||||
total_commits: 83,
|
||||
commits: [{
|
||||
sha: "commit-sha0",
|
||||
html_url: "https://api.github.com/repos/matrix-org/matrix-react-sdk/commit/commit-sha",
|
||||
commit: { message: "This is a commit message" },
|
||||
}],
|
||||
commits: [
|
||||
{
|
||||
sha: "commit-sha0",
|
||||
html_url: "https://api.github.com/repos/matrix-org/matrix-react-sdk/commit/commit-sha",
|
||||
commit: { message: "This is a commit message" },
|
||||
},
|
||||
],
|
||||
files: [],
|
||||
});
|
||||
const jsUrl = "https://riot.im/github/repos/matrix-org/matrix-js-sdk/compare/oldsha3...newsha3";
|
||||
@@ -75,23 +79,26 @@ describe("<ChangelogDialog />", () => {
|
||||
ahead_by: 48,
|
||||
behind_by: 0,
|
||||
total_commits: 48,
|
||||
commits: [{
|
||||
sha: "commit-sha1",
|
||||
html_url: "https://api.github.com/repos/matrix-org/matrix-js-sdk/commit/commit-sha1",
|
||||
commit: { message: "This is a commit message" },
|
||||
}, {
|
||||
sha: "commit-sha2",
|
||||
html_url: "https://api.github.com/repos/matrix-org/matrix-js-sdk/commit/commit-sha2",
|
||||
commit: { message: "This is another commit message" },
|
||||
}],
|
||||
commits: [
|
||||
{
|
||||
sha: "commit-sha1",
|
||||
html_url: "https://api.github.com/repos/matrix-org/matrix-js-sdk/commit/commit-sha1",
|
||||
commit: { message: "This is a commit message" },
|
||||
},
|
||||
{
|
||||
sha: "commit-sha2",
|
||||
html_url: "https://api.github.com/repos/matrix-org/matrix-js-sdk/commit/commit-sha2",
|
||||
commit: { message: "This is another commit message" },
|
||||
},
|
||||
],
|
||||
files: [],
|
||||
});
|
||||
|
||||
const newVersion = "newsha1-react-newsha2-js-newsha3";
|
||||
const oldVersion = "oldsha1-react-oldsha2-js-oldsha3";
|
||||
const { asFragment } = render((
|
||||
<ChangelogDialog newVersion={newVersion} version={oldVersion} onFinished={jest.fn()} />
|
||||
));
|
||||
const { asFragment } = render(
|
||||
<ChangelogDialog newVersion={newVersion} version={oldVersion} onFinished={jest.fn()} />,
|
||||
);
|
||||
|
||||
// Wait for spinners to go away
|
||||
await waitForElementToBeRemoved(screen.getAllByRole("progressbar"));
|
||||
|
||||
@@ -14,33 +14,33 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { mount } from "enzyme";
|
||||
import { mocked } from "jest-mock";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { Room } from 'matrix-js-sdk/src/matrix';
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import ExportDialog from '../../../../src/components/views/dialogs/ExportDialog';
|
||||
import { ExportType, ExportFormat } from '../../../../src/utils/exportUtils/exportUtils';
|
||||
import { createTestClient, mkStubRoom } from '../../../test-utils';
|
||||
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||
import ExportDialog from "../../../../src/components/views/dialogs/ExportDialog";
|
||||
import { ExportType, ExportFormat } from "../../../../src/utils/exportUtils/exportUtils";
|
||||
import { createTestClient, mkStubRoom } from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import HTMLExporter from "../../../../src/utils/exportUtils/HtmlExport";
|
||||
import ChatExport from '../../../../src/customisations/ChatExport';
|
||||
import PlainTextExporter from '../../../../src/utils/exportUtils/PlainTextExport';
|
||||
import ChatExport from "../../../../src/customisations/ChatExport";
|
||||
import PlainTextExporter from "../../../../src/utils/exportUtils/PlainTextExport";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const htmlExporterInstance = ({
|
||||
const htmlExporterInstance = {
|
||||
export: jest.fn().mockResolvedValue({}),
|
||||
});
|
||||
const plainTextExporterInstance = ({
|
||||
};
|
||||
const plainTextExporterInstance = {
|
||||
export: jest.fn().mockResolvedValue({}),
|
||||
});
|
||||
};
|
||||
jest.mock("../../../../src/utils/exportUtils/HtmlExport", () => jest.fn());
|
||||
jest.mock("../../../../src/utils/exportUtils/PlainTextExport", () => jest.fn());
|
||||
|
||||
jest.mock('../../../../src/customisations/ChatExport', () => ({
|
||||
jest.mock("../../../../src/customisations/ChatExport", () => ({
|
||||
getForceChatExportParameters: jest.fn().mockReturnValue({}),
|
||||
}));
|
||||
|
||||
@@ -48,13 +48,13 @@ const ChatExportMock = mocked(ChatExport);
|
||||
const HTMLExporterMock = mocked(HTMLExporter);
|
||||
const PlainTextExporterMock = mocked(PlainTextExporter);
|
||||
|
||||
describe('<ExportDialog />', () => {
|
||||
describe("<ExportDialog />", () => {
|
||||
const mockClient = createTestClient();
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient);
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
|
||||
|
||||
const roomId = 'test:test.org';
|
||||
const roomId = "test:test.org";
|
||||
const defaultProps = {
|
||||
room: mkStubRoom(roomId, 'test', mockClient) as unknown as Room,
|
||||
room: mkStubRoom(roomId, "test", mockClient) as unknown as Room,
|
||||
onFinished: jest.fn(),
|
||||
};
|
||||
|
||||
@@ -68,32 +68,38 @@ describe('<ExportDialog />', () => {
|
||||
const getPrimaryButton = (component) => component.find('[data-testid="dialog-primary-button"]');
|
||||
const getSecondaryButton = (component) => component.find('[data-testid="dialog-cancel-button"]');
|
||||
|
||||
const submitForm = async (component) => act(async () => {
|
||||
getPrimaryButton(component).simulate('click');
|
||||
component.setProps({});
|
||||
});
|
||||
const selectExportFormat = async (component, format: ExportFormat) => act(async () => {
|
||||
getExportFormatInput(component, format).simulate('change');
|
||||
component.setProps({});
|
||||
});
|
||||
const selectExportType = async (component, type: ExportType) => act(async () => {
|
||||
getExportTypeInput(component).simulate('change', { target: { value: type } });
|
||||
component.setProps({});
|
||||
});
|
||||
const setMessageCount = async (component, count: number) => act(async () => {
|
||||
getMessageCountInput(component).simulate('change', { target: { value: count } });
|
||||
component.setProps({});
|
||||
});
|
||||
const submitForm = async (component) =>
|
||||
act(async () => {
|
||||
getPrimaryButton(component).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
const selectExportFormat = async (component, format: ExportFormat) =>
|
||||
act(async () => {
|
||||
getExportFormatInput(component, format).simulate("change");
|
||||
component.setProps({});
|
||||
});
|
||||
const selectExportType = async (component, type: ExportType) =>
|
||||
act(async () => {
|
||||
getExportTypeInput(component).simulate("change", { target: { value: type } });
|
||||
component.setProps({});
|
||||
});
|
||||
const setMessageCount = async (component, count: number) =>
|
||||
act(async () => {
|
||||
getMessageCountInput(component).simulate("change", { target: { value: count } });
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
const setSizeLimit = async (component, limit: number) => act(async () => {
|
||||
getSizeInput(component).simulate('change', { target: { value: limit } });
|
||||
component.setProps({});
|
||||
});
|
||||
const setSizeLimit = async (component, limit: number) =>
|
||||
act(async () => {
|
||||
getSizeInput(component).simulate("change", { target: { value: limit } });
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
const setIncludeAttachments = async (component, checked) => act(async () => {
|
||||
getAttachmentsCheckbox(component).simulate('change', { target: { checked } });
|
||||
component.setProps({});
|
||||
});
|
||||
const setIncludeAttachments = async (component, checked) =>
|
||||
act(async () => {
|
||||
getAttachmentsCheckbox(component).simulate("change", { target: { checked } });
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
HTMLExporterMock.mockClear().mockImplementation(jest.fn().mockReturnValue(htmlExporterInstance));
|
||||
@@ -105,21 +111,21 @@ describe('<ExportDialog />', () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockClear().mockReturnValue({});
|
||||
});
|
||||
|
||||
it('renders export dialog', () => {
|
||||
it("renders export dialog", () => {
|
||||
const component = getComponent();
|
||||
expect(component.find('.mx_ExportDialog')).toMatchSnapshot();
|
||||
expect(component.find(".mx_ExportDialog")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('calls onFinished when cancel button is clicked', () => {
|
||||
it("calls onFinished when cancel button is clicked", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
act(() => {
|
||||
getSecondaryButton(component).simulate('click');
|
||||
getSecondaryButton(component).simulate("click");
|
||||
});
|
||||
expect(onFinished).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('exports room on submit', async () => {
|
||||
it("exports room on submit", async () => {
|
||||
const component = getComponent();
|
||||
await submitForm(component);
|
||||
|
||||
@@ -137,7 +143,7 @@ describe('<ExportDialog />', () => {
|
||||
expect(htmlExporterInstance.export).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('exports room using values set from ForceRoomExportParameters', async () => {
|
||||
it("exports room using values set from ForceRoomExportParameters", async () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
format: ExportFormat.PlainText,
|
||||
range: ExportType.Beginning,
|
||||
@@ -162,35 +168,35 @@ describe('<ExportDialog />', () => {
|
||||
expect(plainTextExporterInstance.export).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders success screen when export is finished', async () => {
|
||||
it("renders success screen when export is finished", async () => {
|
||||
const component = getComponent();
|
||||
await submitForm(component);
|
||||
component.setProps({});
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(component.find('.mx_InfoDialog .mx_Dialog_content')).toMatchSnapshot();
|
||||
expect(component.find(".mx_InfoDialog .mx_Dialog_content")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('export format', () => {
|
||||
it('renders export format with html selected by default', () => {
|
||||
describe("export format", () => {
|
||||
it("renders export format with html selected by default", () => {
|
||||
const component = getComponent();
|
||||
expect(getExportFormatInput(component, ExportFormat.Html).props().checked).toBeTruthy();
|
||||
});
|
||||
|
||||
it('sets export format on radio button click', async () => {
|
||||
it("sets export format on radio button click", async () => {
|
||||
const component = getComponent();
|
||||
await selectExportFormat(component, ExportFormat.PlainText);
|
||||
expect(getExportFormatInput(component, ExportFormat.PlainText).props().checked).toBeTruthy();
|
||||
expect(getExportFormatInput(component, ExportFormat.Html).props().checked).toBeFalsy();
|
||||
});
|
||||
|
||||
it('hides export format input when format is valid in ForceRoomExportParameters', () => {
|
||||
it("hides export format input when format is valid in ForceRoomExportParameters", () => {
|
||||
const component = getComponent();
|
||||
expect(getExportFormatInput(component, ExportFormat.Html).props().checked).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render export format when set in ForceRoomExportParameters', () => {
|
||||
it("does not render export format when set in ForceRoomExportParameters", () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
format: ExportFormat.PlainText,
|
||||
});
|
||||
@@ -199,19 +205,19 @@ describe('<ExportDialog />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('export type', () => {
|
||||
it('renders export type with timeline selected by default', () => {
|
||||
describe("export type", () => {
|
||||
it("renders export type with timeline selected by default", () => {
|
||||
const component = getComponent();
|
||||
expect(getExportTypeInput(component).props().value).toEqual(ExportType.Timeline);
|
||||
});
|
||||
|
||||
it('sets export type on change', async () => {
|
||||
it("sets export type on change", async () => {
|
||||
const component = getComponent();
|
||||
await selectExportType(component, ExportType.Beginning);
|
||||
expect(getExportTypeInput(component).props().value).toEqual(ExportType.Beginning);
|
||||
});
|
||||
|
||||
it('does not render export type when set in ForceRoomExportParameters', () => {
|
||||
it("does not render export type when set in ForceRoomExportParameters", () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
range: ExportType.Beginning,
|
||||
});
|
||||
@@ -219,25 +225,25 @@ describe('<ExportDialog />', () => {
|
||||
expect(getExportTypeInput(component).length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does not render message count input', async () => {
|
||||
it("does not render message count input", async () => {
|
||||
const component = getComponent();
|
||||
expect(getMessageCountInput(component).length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('renders message count input with default value 100 when export type is lastNMessages', async () => {
|
||||
it("renders message count input with default value 100 when export type is lastNMessages", async () => {
|
||||
const component = getComponent();
|
||||
await selectExportType(component, ExportType.LastNMessages);
|
||||
expect(getMessageCountInput(component).props().value).toEqual("100");
|
||||
});
|
||||
|
||||
it('sets message count on change', async () => {
|
||||
it("sets message count on change", async () => {
|
||||
const component = getComponent();
|
||||
await selectExportType(component, ExportType.LastNMessages);
|
||||
await setMessageCount(component, 10);
|
||||
expect(getMessageCountInput(component).props().value).toEqual("10");
|
||||
});
|
||||
|
||||
it('does not export when export type is lastNMessages and message count is falsy', async () => {
|
||||
it("does not export when export type is lastNMessages and message count is falsy", async () => {
|
||||
const component = getComponent();
|
||||
await selectExportType(component, ExportType.LastNMessages);
|
||||
await setMessageCount(component, 0);
|
||||
@@ -246,7 +252,7 @@ describe('<ExportDialog />', () => {
|
||||
expect(htmlExporterInstance.export).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not export when export type is lastNMessages and message count is more than max', async () => {
|
||||
it("does not export when export type is lastNMessages and message count is more than max", async () => {
|
||||
const component = getComponent();
|
||||
await selectExportType(component, ExportType.LastNMessages);
|
||||
await setMessageCount(component, 99999999999);
|
||||
@@ -255,7 +261,7 @@ describe('<ExportDialog />', () => {
|
||||
expect(htmlExporterInstance.export).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('exports when export type is NOT lastNMessages and message count is falsy', async () => {
|
||||
it("exports when export type is NOT lastNMessages and message count is falsy", async () => {
|
||||
const component = getComponent();
|
||||
await selectExportType(component, ExportType.LastNMessages);
|
||||
await setMessageCount(component, 0);
|
||||
@@ -266,19 +272,19 @@ describe('<ExportDialog />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('size limit', () => {
|
||||
it('renders size limit input with default value', () => {
|
||||
describe("size limit", () => {
|
||||
it("renders size limit input with default value", () => {
|
||||
const component = getComponent();
|
||||
expect(getSizeInput(component).props().value).toEqual("8");
|
||||
});
|
||||
|
||||
it('updates size limit on change', async () => {
|
||||
it("updates size limit on change", async () => {
|
||||
const component = getComponent();
|
||||
await setSizeLimit(component, 20);
|
||||
expect(getSizeInput(component).props().value).toEqual("20");
|
||||
});
|
||||
|
||||
it('does not export when size limit is falsy', async () => {
|
||||
it("does not export when size limit is falsy", async () => {
|
||||
const component = getComponent();
|
||||
await setSizeLimit(component, 0);
|
||||
await submitForm(component);
|
||||
@@ -286,7 +292,7 @@ describe('<ExportDialog />', () => {
|
||||
expect(htmlExporterInstance.export).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not export when size limit is larger than max', async () => {
|
||||
it("does not export when size limit is larger than max", async () => {
|
||||
const component = getComponent();
|
||||
await setSizeLimit(component, 2001);
|
||||
await submitForm(component);
|
||||
@@ -294,7 +300,7 @@ describe('<ExportDialog />', () => {
|
||||
expect(htmlExporterInstance.export).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('exports when size limit is max', async () => {
|
||||
it("exports when size limit is max", async () => {
|
||||
const component = getComponent();
|
||||
await setSizeLimit(component, 2000);
|
||||
await submitForm(component);
|
||||
@@ -302,7 +308,7 @@ describe('<ExportDialog />', () => {
|
||||
expect(htmlExporterInstance.export).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not render size limit input when set in ForceRoomExportParameters', () => {
|
||||
it("does not render size limit input when set in ForceRoomExportParameters", () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
sizeMb: 10000,
|
||||
});
|
||||
@@ -313,7 +319,7 @@ describe('<ExportDialog />', () => {
|
||||
/**
|
||||
* 2000mb size limit does not apply when higher limit is configured in config
|
||||
*/
|
||||
it('exports when size limit set in ForceRoomExportParameters is larger than 2000', async () => {
|
||||
it("exports when size limit set in ForceRoomExportParameters is larger than 2000", async () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
sizeMb: 10000,
|
||||
});
|
||||
@@ -324,19 +330,19 @@ describe('<ExportDialog />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('include attachments', () => {
|
||||
it('renders input with default value of false', () => {
|
||||
describe("include attachments", () => {
|
||||
it("renders input with default value of false", () => {
|
||||
const component = getComponent();
|
||||
expect(getAttachmentsCheckbox(component).props().checked).toEqual(false);
|
||||
});
|
||||
|
||||
it('updates include attachments on change', async () => {
|
||||
it("updates include attachments on change", async () => {
|
||||
const component = getComponent();
|
||||
await setIncludeAttachments(component, true);
|
||||
expect(getAttachmentsCheckbox(component).props().checked).toEqual(true);
|
||||
});
|
||||
|
||||
it('does not render input when set in ForceRoomExportParameters', () => {
|
||||
it("does not render input when set in ForceRoomExportParameters", () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
includeAttachments: false,
|
||||
});
|
||||
@@ -345,4 +351,3 @@ describe('<ExportDialog />', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -59,22 +59,22 @@ describe("ForwardDialog", () => {
|
||||
getRoom: jest.fn(),
|
||||
getAccountData: jest.fn().mockReturnValue(accountDataEvent),
|
||||
getPushActionsForEvent: jest.fn(),
|
||||
mxcUrlToHttp: jest.fn().mockReturnValue(''),
|
||||
mxcUrlToHttp: jest.fn().mockReturnValue(""),
|
||||
isRoomEncrypted: jest.fn().mockReturnValue(false),
|
||||
getProfileInfo: jest.fn().mockResolvedValue({
|
||||
displayname: 'Alice',
|
||||
displayname: "Alice",
|
||||
}),
|
||||
decryptEventIfNeeded: jest.fn(),
|
||||
sendEvent: jest.fn(),
|
||||
getClientWellKnown: jest.fn().mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
}),
|
||||
});
|
||||
const defaultRooms = ["a", "A", "b"].map(name => mkStubRoom(name, name, mockClient));
|
||||
const defaultRooms = ["a", "A", "b"].map((name) => mkStubRoom(name, name, mockClient));
|
||||
|
||||
const mountForwardDialog = (message = defaultMessage, rooms = defaultRooms) => {
|
||||
mockClient.getVisibleRooms.mockReturnValue(rooms);
|
||||
mockClient.getRoom.mockImplementation(roomId => rooms.find(room => room.roomId === roomId));
|
||||
mockClient.getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId));
|
||||
|
||||
const wrapper: RenderResult = render(
|
||||
<ForwardDialog
|
||||
@@ -96,7 +96,7 @@ describe("ForwardDialog", () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockRestore();
|
||||
jest.spyOn(MatrixClientPeg, "get").mockRestore();
|
||||
});
|
||||
|
||||
it("shows a preview with us as the sender", async () => {
|
||||
@@ -127,10 +127,13 @@ describe("ForwardDialog", () => {
|
||||
// Make sendEvent require manual resolution so we can see the sending state
|
||||
let finishSend;
|
||||
let cancelSend;
|
||||
mockClient.sendEvent.mockImplementation(<T extends {}>() => new Promise<T>((resolve, reject) => {
|
||||
finishSend = resolve;
|
||||
cancelSend = reject;
|
||||
}));
|
||||
mockClient.sendEvent.mockImplementation(
|
||||
<T extends {}>() =>
|
||||
new Promise<T>((resolve, reject) => {
|
||||
finishSend = resolve;
|
||||
cancelSend = reject;
|
||||
}),
|
||||
);
|
||||
|
||||
let firstButton;
|
||||
let secondButton;
|
||||
@@ -141,28 +144,32 @@ describe("ForwardDialog", () => {
|
||||
|
||||
expect(firstButton.className).toContain("mx_ForwardList_canSend");
|
||||
|
||||
act(() => { fireEvent.click(firstButton); });
|
||||
act(() => {
|
||||
fireEvent.click(firstButton);
|
||||
});
|
||||
update();
|
||||
expect(firstButton.className).toContain("mx_ForwardList_sending");
|
||||
|
||||
await act(async () => {
|
||||
cancelSend();
|
||||
// Wait one tick for the button to realize the send failed
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
});
|
||||
update();
|
||||
expect(firstButton.className).toContain("mx_ForwardList_sendFailed");
|
||||
|
||||
expect(secondButton.className).toContain("mx_ForwardList_canSend");
|
||||
|
||||
act(() => { fireEvent.click(secondButton); });
|
||||
act(() => {
|
||||
fireEvent.click(secondButton);
|
||||
});
|
||||
update();
|
||||
expect(secondButton.className).toContain("mx_ForwardList_sending");
|
||||
|
||||
await act(async () => {
|
||||
finishSend();
|
||||
// Wait one tick for the button to realize the send succeeded
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
});
|
||||
update();
|
||||
expect(secondButton.className).toContain("mx_ForwardList_sent");
|
||||
@@ -203,7 +210,7 @@ describe("ForwardDialog", () => {
|
||||
expect(secondButton.getAttribute("aria-disabled")).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('Location events', () => {
|
||||
describe("Location events", () => {
|
||||
// 14.03.2022 16:15
|
||||
const now = 1647270879403;
|
||||
const roomId = "a";
|
||||
@@ -215,11 +222,11 @@ describe("ForwardDialog", () => {
|
||||
beforeEach(() => {
|
||||
// legacy events will default timestamp to Date.now()
|
||||
// mock a stable now for easy assertion
|
||||
jest.spyOn(Date, 'now').mockReturnValue(now);
|
||||
jest.spyOn(Date, "now").mockReturnValue(now);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(Date, 'now').mockRestore();
|
||||
jest.spyOn(Date, "now").mockRestore();
|
||||
});
|
||||
|
||||
const sendToFirstRoom = (container: HTMLElement): void =>
|
||||
@@ -228,7 +235,7 @@ describe("ForwardDialog", () => {
|
||||
fireEvent.click(sendToFirstRoomButton!);
|
||||
});
|
||||
|
||||
it('converts legacy location events to pin drop shares', async () => {
|
||||
it("converts legacy location events to pin drop shares", async () => {
|
||||
const { container } = mountForwardDialog(legacyLocationEvent);
|
||||
|
||||
expect(container.querySelector(".mx_MLocationBody")).toBeTruthy();
|
||||
@@ -250,11 +257,13 @@ describe("ForwardDialog", () => {
|
||||
},
|
||||
};
|
||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(
|
||||
roomId, legacyLocationEvent.getType(), expectedStrippedContent,
|
||||
roomId,
|
||||
legacyLocationEvent.getType(),
|
||||
expectedStrippedContent,
|
||||
);
|
||||
});
|
||||
|
||||
it('removes personal information from static self location shares', async () => {
|
||||
it("removes personal information from static self location shares", async () => {
|
||||
const { container } = mountForwardDialog(modernLocationEvent);
|
||||
|
||||
expect(container.querySelector(".mx_MLocationBody")).toBeTruthy();
|
||||
@@ -275,13 +284,15 @@ describe("ForwardDialog", () => {
|
||||
},
|
||||
};
|
||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(
|
||||
roomId, modernLocationEvent.getType(), expectedStrippedContent,
|
||||
roomId,
|
||||
modernLocationEvent.getType(),
|
||||
expectedStrippedContent,
|
||||
);
|
||||
});
|
||||
|
||||
it('forwards beacon location as a pin drop event', async () => {
|
||||
it("forwards beacon location as a pin drop event", async () => {
|
||||
const timestamp = 123456;
|
||||
const beaconEvent = makeBeaconEvent('@alice:server.org', { geoUri, timestamp });
|
||||
const beaconEvent = makeBeaconEvent("@alice:server.org", { geoUri, timestamp });
|
||||
const text = `Location ${geoUri} at ${new Date(timestamp).toISOString()}`;
|
||||
const expectedContent = {
|
||||
msgtype: "m.location",
|
||||
@@ -301,12 +312,10 @@ describe("ForwardDialog", () => {
|
||||
|
||||
sendToFirstRoom(container);
|
||||
|
||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(
|
||||
roomId, EventType.RoomMessage, expectedContent,
|
||||
);
|
||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, EventType.RoomMessage, expectedContent);
|
||||
});
|
||||
|
||||
it('forwards pin drop event', async () => {
|
||||
it("forwards pin drop event", async () => {
|
||||
const { container } = mountForwardDialog(pinDropLocationEvent);
|
||||
|
||||
expect(container.querySelector(".mx_MLocationBody")).toBeTruthy();
|
||||
@@ -314,7 +323,9 @@ describe("ForwardDialog", () => {
|
||||
sendToFirstRoom(container);
|
||||
|
||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(
|
||||
roomId, pinDropLocationEvent.getType(), pinDropLocationEvent.getContent(),
|
||||
roomId,
|
||||
pinDropLocationEvent.getType(),
|
||||
pinDropLocationEvent.getContent(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,17 +15,17 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import React from "react";
|
||||
import { act } from "react-dom/test-utils";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount, ReactWrapper } from 'enzyme';
|
||||
import { mount, ReactWrapper } from "enzyme";
|
||||
|
||||
import InteractiveAuthDialog from "../../../../src/components/views/dialogs/InteractiveAuthDialog";
|
||||
import { flushPromises, getMockClientWithEventEmitter, unmockClientPeg } from '../../../test-utils';
|
||||
import { flushPromises, getMockClientWithEventEmitter, unmockClientPeg } from "../../../test-utils";
|
||||
|
||||
describe('InteractiveAuthDialog', function() {
|
||||
describe("InteractiveAuthDialog", function () {
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
generateClientSecret: jest.fn().mockReturnValue('t35tcl1Ent5ECr3T'),
|
||||
generateClientSecret: jest.fn().mockReturnValue("t35tcl1Ent5ECr3T"),
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
@@ -33,12 +33,9 @@ describe('InteractiveAuthDialog', function() {
|
||||
makeRequest: jest.fn().mockResolvedValue(undefined),
|
||||
onFinished: jest.fn(),
|
||||
};
|
||||
const getComponent = (props = {}) => mount(<InteractiveAuthDialog
|
||||
{...defaultProps}
|
||||
{...props}
|
||||
/>);
|
||||
const getComponent = (props = {}) => mount(<InteractiveAuthDialog {...defaultProps} {...props} />);
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
jest.clearAllMocks();
|
||||
mockClient.credentials = null;
|
||||
});
|
||||
@@ -49,16 +46,14 @@ describe('InteractiveAuthDialog', function() {
|
||||
|
||||
const getSubmitButton = (wrapper: ReactWrapper) => wrapper.find('[type="submit"]').at(0);
|
||||
|
||||
it('Should successfully complete a password flow', async () => {
|
||||
it("Should successfully complete a password flow", async () => {
|
||||
const onFinished = jest.fn();
|
||||
const makeRequest = jest.fn().mockResolvedValue({ a: 1 });
|
||||
|
||||
mockClient.credentials = { userId: "@user:id" };
|
||||
const authData = {
|
||||
session: "sess",
|
||||
flows: [
|
||||
{ "stages": ["m.login.password"] },
|
||||
],
|
||||
flows: [{ stages: ["m.login.password"] }],
|
||||
};
|
||||
|
||||
const wrapper = getComponent({ makeRequest, onFinished, authData });
|
||||
@@ -66,7 +61,7 @@ describe('InteractiveAuthDialog', function() {
|
||||
const passwordNode = wrapper.find('input[type="password"]').at(0);
|
||||
const submitNode = getSubmitButton(wrapper);
|
||||
|
||||
const formNode = wrapper.find('form').at(0);
|
||||
const formNode = wrapper.find("form").at(0);
|
||||
expect(passwordNode).toBeTruthy();
|
||||
expect(submitNode).toBeTruthy();
|
||||
|
||||
@@ -75,7 +70,7 @@ describe('InteractiveAuthDialog', function() {
|
||||
|
||||
// put something in the password box
|
||||
act(() => {
|
||||
passwordNode.simulate('change', { target: { value: "s3kr3t" } });
|
||||
passwordNode.simulate("change", { target: { value: "s3kr3t" } });
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
@@ -84,22 +79,24 @@ describe('InteractiveAuthDialog', function() {
|
||||
|
||||
// hit enter; that should trigger a request
|
||||
act(() => {
|
||||
formNode.simulate('submit');
|
||||
formNode.simulate("submit");
|
||||
});
|
||||
|
||||
// wait for auth request to resolve
|
||||
await flushPromises();
|
||||
|
||||
expect(makeRequest).toHaveBeenCalledTimes(1);
|
||||
expect(makeRequest).toBeCalledWith(expect.objectContaining({
|
||||
session: "sess",
|
||||
type: "m.login.password",
|
||||
password: "s3kr3t",
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
user: "@user:id",
|
||||
},
|
||||
}));
|
||||
expect(makeRequest).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
session: "sess",
|
||||
type: "m.login.password",
|
||||
password: "s3kr3t",
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
user: "@user:id",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(onFinished).toBeCalledTimes(1);
|
||||
expect(onFinished).toBeCalledWith(true, { a: 1 });
|
||||
|
||||
@@ -28,9 +28,11 @@ import { ValidatedServerConfig } from "../../../../src/utils/ValidatedServerConf
|
||||
import { IConfigOptions } from "../../../../src/IConfigOptions";
|
||||
|
||||
const mockGetAccessToken = jest.fn().mockResolvedValue("getAccessToken");
|
||||
jest.mock("../../../../src/IdentityAuthClient", () => jest.fn().mockImplementation(() => ({
|
||||
getAccessToken: mockGetAccessToken,
|
||||
})));
|
||||
jest.mock("../../../../src/IdentityAuthClient", () =>
|
||||
jest.fn().mockImplementation(() => ({
|
||||
getAccessToken: mockGetAccessToken,
|
||||
})),
|
||||
);
|
||||
|
||||
describe("InviteDialog", () => {
|
||||
const roomId = "!111111111111111111:example.org";
|
||||
@@ -43,7 +45,7 @@ describe("InviteDialog", () => {
|
||||
getRooms: jest.fn(),
|
||||
getAccountData: jest.fn(),
|
||||
getPushActionsForEvent: jest.fn(),
|
||||
mxcUrlToHttp: jest.fn().mockReturnValue(''),
|
||||
mxcUrlToHttp: jest.fn().mockReturnValue(""),
|
||||
isRoomEncrypted: jest.fn().mockReturnValue(false),
|
||||
getProfileInfo: jest.fn().mockRejectedValue({ errcode: "" }),
|
||||
getIdentityServerUrl: jest.fn(),
|
||||
@@ -70,58 +72,46 @@ describe("InviteDialog", () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockRestore();
|
||||
jest.spyOn(MatrixClientPeg, "get").mockRestore();
|
||||
});
|
||||
|
||||
it("should label with space name", () => {
|
||||
mockClient.getRoom(roomId).isSpaceRoom = jest.fn().mockReturnValue(true);
|
||||
mockClient.getRoom(roomId).getType = jest.fn().mockReturnValue(RoomType.Space);
|
||||
mockClient.getRoom(roomId).name = "Space";
|
||||
render((
|
||||
<InviteDialog
|
||||
kind={KIND_INVITE}
|
||||
roomId={roomId}
|
||||
onFinished={jest.fn()}
|
||||
/>
|
||||
));
|
||||
render(<InviteDialog kind={KIND_INVITE} roomId={roomId} onFinished={jest.fn()} />);
|
||||
|
||||
expect(screen.queryByText("Invite to Space")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should label with room name", () => {
|
||||
render((
|
||||
<InviteDialog
|
||||
kind={KIND_INVITE}
|
||||
roomId={roomId}
|
||||
onFinished={jest.fn()}
|
||||
/>
|
||||
));
|
||||
render(<InviteDialog kind={KIND_INVITE} roomId={roomId} onFinished={jest.fn()} />);
|
||||
|
||||
expect(screen.queryByText("Invite to Room")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should suggest valid MXIDs even if unknown", async () => {
|
||||
render((
|
||||
render(
|
||||
<InviteDialog
|
||||
kind={KIND_INVITE}
|
||||
roomId={roomId}
|
||||
onFinished={jest.fn()}
|
||||
initialText="@localpart:server.tld"
|
||||
/>
|
||||
));
|
||||
/>,
|
||||
);
|
||||
|
||||
await screen.findAllByText("@localpart:server.tld"); // Using findAllByText as the MXID is used for name too
|
||||
});
|
||||
|
||||
it("should not suggest invalid MXIDs", () => {
|
||||
render((
|
||||
render(
|
||||
<InviteDialog
|
||||
kind={KIND_INVITE}
|
||||
roomId={roomId}
|
||||
onFinished={jest.fn()}
|
||||
initialText="@localpart:server:tld"
|
||||
/>
|
||||
));
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText("@localpart:server:tld")).toBeFalsy();
|
||||
});
|
||||
@@ -138,14 +128,9 @@ describe("InviteDialog", () => {
|
||||
avatar_url: "mxc://foo/bar",
|
||||
});
|
||||
|
||||
render((
|
||||
<InviteDialog
|
||||
kind={KIND_INVITE}
|
||||
roomId={roomId}
|
||||
onFinished={jest.fn()}
|
||||
initialText="foobar@email.com"
|
||||
/>
|
||||
));
|
||||
render(
|
||||
<InviteDialog kind={KIND_INVITE} roomId={roomId} onFinished={jest.fn()} initialText="foobar@email.com" />,
|
||||
);
|
||||
|
||||
await screen.findByText("Mr. Foo");
|
||||
await screen.findByText("@foobar:server");
|
||||
@@ -157,14 +142,9 @@ describe("InviteDialog", () => {
|
||||
mockClient.getIdentityServerUrl.mockReturnValue("https://identity-server");
|
||||
mockClient.lookupThreePid.mockResolvedValue({});
|
||||
|
||||
render((
|
||||
<InviteDialog
|
||||
kind={KIND_INVITE}
|
||||
roomId={roomId}
|
||||
onFinished={jest.fn()}
|
||||
initialText="foobar@email.com"
|
||||
/>
|
||||
));
|
||||
render(
|
||||
<InviteDialog kind={KIND_INVITE} roomId={roomId} onFinished={jest.fn()} initialText="foobar@email.com" />,
|
||||
);
|
||||
|
||||
await screen.findByText("foobar@email.com");
|
||||
await screen.findByText("Invite by email");
|
||||
|
||||
@@ -54,16 +54,14 @@ interface MockClientOptions {
|
||||
users?: IUserChunkMember[];
|
||||
}
|
||||
|
||||
function mockClient(
|
||||
{
|
||||
userId = "testuser",
|
||||
homeserver = "example.tld",
|
||||
thirdPartyProtocols = {},
|
||||
rooms = [],
|
||||
members = [],
|
||||
users = [],
|
||||
}: MockClientOptions = {},
|
||||
): MatrixClient {
|
||||
function mockClient({
|
||||
userId = "testuser",
|
||||
homeserver = "example.tld",
|
||||
thirdPartyProtocols = {},
|
||||
rooms = [],
|
||||
members = [],
|
||||
users = [],
|
||||
}: MockClientOptions = {}): MatrixClient {
|
||||
stubClient();
|
||||
const cli = MatrixClientPeg.get();
|
||||
MatrixClientPeg.getHomeserverName = jest.fn(() => homeserver);
|
||||
@@ -72,13 +70,15 @@ function mockClient(
|
||||
cli.getThirdpartyProtocols = jest.fn(() => Promise.resolve(thirdPartyProtocols));
|
||||
cli.publicRooms = jest.fn((options) => {
|
||||
const searchTerm = options?.filter?.generic_search_term?.toLowerCase();
|
||||
const chunk = rooms.filter(it =>
|
||||
!searchTerm ||
|
||||
it.room_id.toLowerCase().includes(searchTerm) ||
|
||||
it.name?.toLowerCase().includes(searchTerm) ||
|
||||
sanitizeHtml(it?.topic, { allowedTags: [] }).toLowerCase().includes(searchTerm) ||
|
||||
it.canonical_alias?.toLowerCase().includes(searchTerm) ||
|
||||
it.aliases?.find(alias => alias.toLowerCase().includes(searchTerm)));
|
||||
const chunk = rooms.filter(
|
||||
(it) =>
|
||||
!searchTerm ||
|
||||
it.room_id.toLowerCase().includes(searchTerm) ||
|
||||
it.name?.toLowerCase().includes(searchTerm) ||
|
||||
sanitizeHtml(it?.topic, { allowedTags: [] }).toLowerCase().includes(searchTerm) ||
|
||||
it.canonical_alias?.toLowerCase().includes(searchTerm) ||
|
||||
it.aliases?.find((alias) => alias.toLowerCase().includes(searchTerm)),
|
||||
);
|
||||
return Promise.resolve({
|
||||
chunk,
|
||||
total_room_count_estimate: chunk.length,
|
||||
@@ -86,16 +86,19 @@ function mockClient(
|
||||
});
|
||||
cli.searchUserDirectory = jest.fn(({ term, limit }) => {
|
||||
const searchTerm = term?.toLowerCase();
|
||||
const results = users.filter(it => !searchTerm ||
|
||||
it.user_id.toLowerCase().includes(searchTerm) ||
|
||||
it.display_name.toLowerCase().includes(searchTerm));
|
||||
const results = users.filter(
|
||||
(it) =>
|
||||
!searchTerm ||
|
||||
it.user_id.toLowerCase().includes(searchTerm) ||
|
||||
it.display_name.toLowerCase().includes(searchTerm),
|
||||
);
|
||||
return Promise.resolve({
|
||||
results: results.slice(0, limit ?? +Infinity),
|
||||
limited: limit && limit < results.length,
|
||||
});
|
||||
});
|
||||
cli.getProfileInfo = jest.fn(async (userId) => {
|
||||
const member = members.find(it => it.userId === userId);
|
||||
const member = members.find((it) => it.userId === userId);
|
||||
if (member) {
|
||||
return Promise.resolve({
|
||||
displayname: member.rawDisplayName,
|
||||
@@ -144,11 +147,7 @@ describe("Spotlight Dialog", () => {
|
||||
|
||||
describe("should apply filters supplied via props", () => {
|
||||
it("without filter", async () => {
|
||||
const wrapper = mount(
|
||||
<SpotlightDialog
|
||||
initialFilter={null}
|
||||
onFinished={() => null} />,
|
||||
);
|
||||
const wrapper = mount(<SpotlightDialog initialFilter={null} onFinished={() => null} />);
|
||||
await act(async () => {
|
||||
await sleep(200);
|
||||
});
|
||||
@@ -160,11 +159,7 @@ describe("Spotlight Dialog", () => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
it("with public room filter", async () => {
|
||||
const wrapper = mount(
|
||||
<SpotlightDialog
|
||||
initialFilter={Filter.PublicRooms}
|
||||
onFinished={() => null} />,
|
||||
);
|
||||
const wrapper = mount(<SpotlightDialog initialFilter={Filter.PublicRooms} onFinished={() => null} />);
|
||||
await act(async () => {
|
||||
await sleep(200);
|
||||
});
|
||||
@@ -186,7 +181,8 @@ describe("Spotlight Dialog", () => {
|
||||
<SpotlightDialog
|
||||
initialFilter={Filter.People}
|
||||
initialText={testPerson.display_name}
|
||||
onFinished={() => null} />,
|
||||
onFinished={() => null}
|
||||
/>,
|
||||
);
|
||||
await act(async () => {
|
||||
await sleep(200);
|
||||
@@ -208,10 +204,7 @@ describe("Spotlight Dialog", () => {
|
||||
|
||||
describe("should apply manually selected filter", () => {
|
||||
it("with public rooms", async () => {
|
||||
const wrapper = mount(
|
||||
<SpotlightDialog
|
||||
onFinished={() => null} />,
|
||||
);
|
||||
const wrapper = mount(<SpotlightDialog onFinished={() => null} />);
|
||||
await act(async () => {
|
||||
await sleep(1);
|
||||
});
|
||||
@@ -234,11 +227,7 @@ describe("Spotlight Dialog", () => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
it("with people", async () => {
|
||||
const wrapper = mount(
|
||||
<SpotlightDialog
|
||||
initialText={testPerson.display_name}
|
||||
onFinished={() => null} />,
|
||||
);
|
||||
const wrapper = mount(<SpotlightDialog initialText={testPerson.display_name} onFinished={() => null} />);
|
||||
await act(async () => {
|
||||
await sleep(1);
|
||||
});
|
||||
@@ -264,11 +253,7 @@ describe("Spotlight Dialog", () => {
|
||||
|
||||
describe("should allow clearing filter manually", () => {
|
||||
it("with public room filter", async () => {
|
||||
const wrapper = mount(
|
||||
<SpotlightDialog
|
||||
initialFilter={Filter.PublicRooms}
|
||||
onFinished={() => null} />,
|
||||
);
|
||||
const wrapper = mount(<SpotlightDialog initialFilter={Filter.PublicRooms} onFinished={() => null} />);
|
||||
await act(async () => {
|
||||
await sleep(200);
|
||||
});
|
||||
@@ -294,7 +279,8 @@ describe("Spotlight Dialog", () => {
|
||||
<SpotlightDialog
|
||||
initialFilter={Filter.People}
|
||||
initialText={testPerson.display_name}
|
||||
onFinished={() => null} />,
|
||||
onFinished={() => null}
|
||||
/>,
|
||||
);
|
||||
await act(async () => {
|
||||
await sleep(200);
|
||||
@@ -323,11 +309,7 @@ describe("Spotlight Dialog", () => {
|
||||
let options: ReactWrapper;
|
||||
|
||||
beforeAll(async () => {
|
||||
wrapper = mount(
|
||||
<SpotlightDialog
|
||||
initialText="test23"
|
||||
onFinished={() => null} />,
|
||||
);
|
||||
wrapper = mount(<SpotlightDialog initialText="test23" onFinished={() => null} />);
|
||||
await act(async () => {
|
||||
await sleep(200);
|
||||
});
|
||||
@@ -357,7 +339,8 @@ describe("Spotlight Dialog", () => {
|
||||
<SpotlightDialog
|
||||
initialFilter={Filter.People}
|
||||
initialText={testPerson.display_name}
|
||||
onFinished={() => null} />,
|
||||
onFinished={() => null}
|
||||
/>,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
|
||||
@@ -14,30 +14,30 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ReactElement } from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { mocked } from 'jest-mock';
|
||||
import React, { ReactElement } from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import SettingsStore, { CallbackFn } from '../../../../src/settings/SettingsStore';
|
||||
import SdkConfig from '../../../../src/SdkConfig';
|
||||
import { UserTab } from '../../../../src/components/views/dialogs/UserTab';
|
||||
import UserSettingsDialog from '../../../../src/components/views/dialogs/UserSettingsDialog';
|
||||
import { IDialogProps } from '../../../../src/components/views/dialogs/IDialogProps';
|
||||
import SettingsStore, { CallbackFn } from "../../../../src/settings/SettingsStore";
|
||||
import SdkConfig from "../../../../src/SdkConfig";
|
||||
import { UserTab } from "../../../../src/components/views/dialogs/UserTab";
|
||||
import UserSettingsDialog from "../../../../src/components/views/dialogs/UserSettingsDialog";
|
||||
import { IDialogProps } from "../../../../src/components/views/dialogs/IDialogProps";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsUser,
|
||||
mockClientMethodsServer,
|
||||
mockPlatformPeg,
|
||||
} from '../../../test-utils';
|
||||
import { UIFeature } from '../../../../src/settings/UIFeature';
|
||||
import { SettingLevel } from '../../../../src/settings/SettingLevel';
|
||||
} from "../../../test-utils";
|
||||
import { UIFeature } from "../../../../src/settings/UIFeature";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
|
||||
mockPlatformPeg({
|
||||
supportsSpellCheckSettings: jest.fn().mockReturnValue(false),
|
||||
getAppVersion: jest.fn().mockResolvedValue('1'),
|
||||
getAppVersion: jest.fn().mockResolvedValue("1"),
|
||||
});
|
||||
|
||||
jest.mock('../../../../src/settings/SettingsStore', () => ({
|
||||
jest.mock("../../../../src/settings/SettingsStore", () => ({
|
||||
getValue: jest.fn(),
|
||||
getValueAt: jest.fn(),
|
||||
canSetValue: jest.fn(),
|
||||
@@ -48,12 +48,12 @@ jest.mock('../../../../src/settings/SettingsStore', () => ({
|
||||
getBetaInfo: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../src/SdkConfig', () => ({
|
||||
jest.mock("../../../../src/SdkConfig", () => ({
|
||||
get: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('<UserSettingsDialog />', () => {
|
||||
const userId = '@alice:server.org';
|
||||
describe("<UserSettingsDialog />", () => {
|
||||
const userId = "@alice:server.org";
|
||||
const mockSettingsStore = mocked(SettingsStore);
|
||||
const mockSdkConfig = mocked(SdkConfig);
|
||||
getMockClientWithEventEmitter({
|
||||
@@ -62,7 +62,7 @@ describe('<UserSettingsDialog />', () => {
|
||||
});
|
||||
|
||||
const defaultProps = { onFinished: jest.fn() };
|
||||
const getComponent = (props: Partial<IDialogProps & {initialTabId?: UserTab}> = {}): ReactElement => (
|
||||
const getComponent = (props: Partial<IDialogProps & { initialTabId?: UserTab }> = {}): ReactElement => (
|
||||
<UserSettingsDialog {...defaultProps} {...props} />
|
||||
);
|
||||
|
||||
@@ -70,52 +70,52 @@ describe('<UserSettingsDialog />', () => {
|
||||
jest.clearAllMocks();
|
||||
mockSettingsStore.getValue.mockReturnValue(false);
|
||||
mockSettingsStore.getFeatureSettingNames.mockReturnValue([]);
|
||||
mockSdkConfig.get.mockReturnValue({ brand: 'Test' });
|
||||
mockSdkConfig.get.mockReturnValue({ brand: "Test" });
|
||||
});
|
||||
|
||||
const getActiveTabLabel = (container) => container.querySelector('.mx_TabbedView_tabLabel_active').textContent;
|
||||
const getActiveTabHeading = (container) => container.querySelector('.mx_SettingsTab_heading').textContent;
|
||||
const getActiveTabLabel = (container) => container.querySelector(".mx_TabbedView_tabLabel_active").textContent;
|
||||
const getActiveTabHeading = (container) => container.querySelector(".mx_SettingsTab_heading").textContent;
|
||||
|
||||
it('should render general settings tab when no initialTabId', () => {
|
||||
it("should render general settings tab when no initialTabId", () => {
|
||||
const { container } = render(getComponent());
|
||||
|
||||
expect(getActiveTabLabel(container)).toEqual('General');
|
||||
expect(getActiveTabHeading(container)).toEqual('General');
|
||||
expect(getActiveTabLabel(container)).toEqual("General");
|
||||
expect(getActiveTabHeading(container)).toEqual("General");
|
||||
});
|
||||
|
||||
it('should render initial tab when initialTabId is set', () => {
|
||||
it("should render initial tab when initialTabId is set", () => {
|
||||
const { container } = render(getComponent({ initialTabId: UserTab.Help }));
|
||||
|
||||
expect(getActiveTabLabel(container)).toEqual('Help & About');
|
||||
expect(getActiveTabHeading(container)).toEqual('Help & About');
|
||||
expect(getActiveTabLabel(container)).toEqual("Help & About");
|
||||
expect(getActiveTabHeading(container)).toEqual("Help & About");
|
||||
});
|
||||
|
||||
it('should render general tab if initialTabId tab cannot be rendered', () => {
|
||||
it("should render general tab if initialTabId tab cannot be rendered", () => {
|
||||
// mjolnir tab is only rendered in some configs
|
||||
const { container } = render(getComponent({ initialTabId: UserTab.Mjolnir }));
|
||||
|
||||
expect(getActiveTabLabel(container)).toEqual('General');
|
||||
expect(getActiveTabHeading(container)).toEqual('General');
|
||||
expect(getActiveTabLabel(container)).toEqual("General");
|
||||
expect(getActiveTabHeading(container)).toEqual("General");
|
||||
});
|
||||
|
||||
it('renders tabs correctly', () => {
|
||||
it("renders tabs correctly", () => {
|
||||
const { container } = render(getComponent());
|
||||
expect(container.querySelectorAll('.mx_TabbedView_tabLabel')).toMatchSnapshot();
|
||||
expect(container.querySelectorAll(".mx_TabbedView_tabLabel")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders ignored users tab when feature_mjolnir is enabled', () => {
|
||||
it("renders ignored users tab when feature_mjolnir is enabled", () => {
|
||||
mockSettingsStore.getValue.mockImplementation((settingName): any => settingName === "feature_mjolnir");
|
||||
const { getByTestId } = render(getComponent());
|
||||
expect(getByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders voip tab when voip is enabled', () => {
|
||||
it("renders voip tab when voip is enabled", () => {
|
||||
mockSettingsStore.getValue.mockImplementation((settingName): any => settingName === UIFeature.Voip);
|
||||
const { getByTestId } = render(getComponent());
|
||||
expect(getByTestId(`settings-tab-${UserTab.Voice}`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders session manager tab when enabled', () => {
|
||||
it("renders session manager tab when enabled", () => {
|
||||
mockSettingsStore.getValue.mockImplementation((settingName): any => {
|
||||
return settingName === "feature_new_device_manager";
|
||||
});
|
||||
@@ -123,23 +123,23 @@ describe('<UserSettingsDialog />', () => {
|
||||
expect(getByTestId(`settings-tab-${UserTab.SessionManager}`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders labs tab when show_labs_settings is enabled in config', () => {
|
||||
it("renders labs tab when show_labs_settings is enabled in config", () => {
|
||||
// @ts-ignore simplified test stub
|
||||
mockSdkConfig.get.mockImplementation((configName) => configName === "show_labs_settings");
|
||||
const { getByTestId } = render(getComponent());
|
||||
expect(getByTestId(`settings-tab-${UserTab.Labs}`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders labs tab when some feature is in beta', () => {
|
||||
mockSettingsStore.getFeatureSettingNames.mockReturnValue(['feature_beta_setting', 'feature_just_normal_labs']);
|
||||
mockSettingsStore.getBetaInfo.mockImplementation(
|
||||
(settingName) => settingName === 'feature_beta_setting' ? {} as any : undefined,
|
||||
it("renders labs tab when some feature is in beta", () => {
|
||||
mockSettingsStore.getFeatureSettingNames.mockReturnValue(["feature_beta_setting", "feature_just_normal_labs"]);
|
||||
mockSettingsStore.getBetaInfo.mockImplementation((settingName) =>
|
||||
settingName === "feature_beta_setting" ? ({} as any) : undefined,
|
||||
);
|
||||
const { getByTestId } = render(getComponent());
|
||||
expect(getByTestId(`settings-tab-${UserTab.Labs}`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('watches settings', () => {
|
||||
it("watches settings", () => {
|
||||
const watchSettingCallbacks: Record<string, CallbackFn> = {};
|
||||
|
||||
mockSettingsStore.watchSetting.mockImplementation((settingName, roomId, callback) => {
|
||||
@@ -150,17 +150,21 @@ describe('<UserSettingsDialog />', () => {
|
||||
const { queryByTestId, unmount } = render(getComponent());
|
||||
expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeFalsy();
|
||||
|
||||
expect(mockSettingsStore.watchSetting.mock.calls[0][0]).toEqual('feature_mjolnir');
|
||||
expect(mockSettingsStore.watchSetting.mock.calls[1][0]).toEqual('feature_new_device_manager');
|
||||
expect(mockSettingsStore.watchSetting.mock.calls[0][0]).toEqual("feature_mjolnir");
|
||||
expect(mockSettingsStore.watchSetting.mock.calls[1][0]).toEqual("feature_new_device_manager");
|
||||
|
||||
// call the watch setting callback
|
||||
watchSettingCallbacks["feature_mjolnir"]("feature_mjolnir", '', SettingLevel.ACCOUNT, true, true);
|
||||
watchSettingCallbacks["feature_mjolnir"]("feature_mjolnir", "", SettingLevel.ACCOUNT, true, true);
|
||||
// tab is rendered now
|
||||
expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeTruthy();
|
||||
|
||||
// call the watch setting callback
|
||||
watchSettingCallbacks["feature_new_device_manager"](
|
||||
"feature_new_device_manager", '', SettingLevel.ACCOUNT, true, true,
|
||||
"feature_new_device_manager",
|
||||
"",
|
||||
SettingLevel.ACCOUNT,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
// tab is rendered now
|
||||
expect(queryByTestId(`settings-tab-${UserTab.SessionManager}`)).toBeTruthy();
|
||||
@@ -168,7 +172,7 @@ describe('<UserSettingsDialog />', () => {
|
||||
unmount();
|
||||
|
||||
// unwatches settings on unmount
|
||||
expect(mockSettingsStore.unwatchSetting).toHaveBeenCalledWith('mock-watcher-id-feature_mjolnir');
|
||||
expect(mockSettingsStore.unwatchSetting).toHaveBeenCalledWith('mock-watcher-id-feature_new_device_manager');
|
||||
expect(mockSettingsStore.unwatchSetting).toHaveBeenCalledWith("mock-watcher-id-feature_mjolnir");
|
||||
expect(mockSettingsStore.unwatchSetting).toHaveBeenCalledWith("mock-watcher-id-feature_new_device_manager");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,19 +22,21 @@ import { PublicRoomResultDetails } from "../../../../../src/components/views/dia
|
||||
|
||||
describe("PublicRoomResultDetails", () => {
|
||||
it("renders", () => {
|
||||
const { asFragment } = render(<PublicRoomResultDetails
|
||||
room={{
|
||||
room_id: "room-id",
|
||||
name: "hello?",
|
||||
canonical_alias: "canonical-alias",
|
||||
world_readable: true,
|
||||
guest_can_join: false,
|
||||
num_joined_members: 666,
|
||||
}}
|
||||
labelId="label-id"
|
||||
descriptionId="description-id"
|
||||
detailsId="details-id"
|
||||
/>);
|
||||
const { asFragment } = render(
|
||||
<PublicRoomResultDetails
|
||||
room={{
|
||||
room_id: "room-id",
|
||||
name: "hello?",
|
||||
canonical_alias: "canonical-alias",
|
||||
world_readable: true,
|
||||
guest_can_join: false,
|
||||
num_joined_members: 666,
|
||||
}}
|
||||
labelId="label-id"
|
||||
descriptionId="description-id"
|
||||
detailsId="details-id"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
@@ -57,12 +59,14 @@ describe("PublicRoomResultDetails", () => {
|
||||
...partialPublicRoomChunk,
|
||||
};
|
||||
|
||||
const { asFragment } = render(<PublicRoomResultDetails
|
||||
room={roomChunk}
|
||||
labelId="label-id"
|
||||
descriptionId="description-id"
|
||||
detailsId="details-id"
|
||||
/>);
|
||||
const { asFragment } = render(
|
||||
<PublicRoomResultDetails
|
||||
room={roomChunk}
|
||||
labelId="label-id"
|
||||
descriptionId="description-id"
|
||||
detailsId="details-id"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -14,22 +14,21 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mount } from "enzyme";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import AccessibleButton from '../../../../src/components/views/elements/AccessibleButton';
|
||||
import { Key } from '../../../../src/Keyboard';
|
||||
import { mockPlatformPeg, unmockPlatformPeg } from '../../../test-utils';
|
||||
import AccessibleButton from "../../../../src/components/views/elements/AccessibleButton";
|
||||
import { Key } from "../../../../src/Keyboard";
|
||||
import { mockPlatformPeg, unmockPlatformPeg } from "../../../test-utils";
|
||||
|
||||
describe('<AccessibleButton />', () => {
|
||||
describe("<AccessibleButton />", () => {
|
||||
const defaultProps = {
|
||||
onClick: jest.fn(),
|
||||
children: 'i am a button',
|
||||
children: "i am a button",
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<AccessibleButton {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => mount(<AccessibleButton {...defaultProps} {...props} />);
|
||||
|
||||
beforeEach(() => {
|
||||
mockPlatformPeg();
|
||||
@@ -39,66 +38,67 @@ describe('<AccessibleButton />', () => {
|
||||
unmockPlatformPeg();
|
||||
});
|
||||
|
||||
const makeKeyboardEvent = (key: string) => ({
|
||||
key,
|
||||
stopPropagation: jest.fn(),
|
||||
preventDefault: jest.fn(),
|
||||
}) as unknown as KeyboardEvent;
|
||||
const makeKeyboardEvent = (key: string) =>
|
||||
({
|
||||
key,
|
||||
stopPropagation: jest.fn(),
|
||||
preventDefault: jest.fn(),
|
||||
} as unknown as KeyboardEvent);
|
||||
|
||||
it('renders div with role button by default', () => {
|
||||
it("renders div with role button by default", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders a button element', () => {
|
||||
const component = getComponent({ element: 'button' });
|
||||
it("renders a button element", () => {
|
||||
const component = getComponent({ element: "button" });
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders with correct classes when button has kind', () => {
|
||||
it("renders with correct classes when button has kind", () => {
|
||||
const component = getComponent({
|
||||
kind: 'primary',
|
||||
kind: "primary",
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('disables button correctly', () => {
|
||||
it("disables button correctly", () => {
|
||||
const onClick = jest.fn();
|
||||
const component = getComponent({
|
||||
onClick,
|
||||
disabled: true,
|
||||
});
|
||||
expect(component.find('.mx_AccessibleButton').props().disabled).toBeTruthy();
|
||||
expect(component.find('.mx_AccessibleButton').props()['aria-disabled']).toBeTruthy();
|
||||
expect(component.find(".mx_AccessibleButton").props().disabled).toBeTruthy();
|
||||
expect(component.find(".mx_AccessibleButton").props()["aria-disabled"]).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
component.simulate('click');
|
||||
component.simulate("click");
|
||||
});
|
||||
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
|
||||
act(() => {
|
||||
const keydownEvent = makeKeyboardEvent(Key.ENTER);
|
||||
component.simulate('keydown', keydownEvent);
|
||||
component.simulate("keydown", keydownEvent);
|
||||
});
|
||||
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls onClick handler on button click', () => {
|
||||
it("calls onClick handler on button click", () => {
|
||||
const onClick = jest.fn();
|
||||
const component = getComponent({
|
||||
onClick,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
component.simulate('click');
|
||||
component.simulate("click");
|
||||
});
|
||||
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls onClick handler on button mousedown when triggerOnMousedown is passed', () => {
|
||||
it("calls onClick handler on button mousedown when triggerOnMousedown is passed", () => {
|
||||
const onClick = jest.fn();
|
||||
const component = getComponent({
|
||||
onClick,
|
||||
@@ -106,14 +106,14 @@ describe('<AccessibleButton />', () => {
|
||||
});
|
||||
|
||||
act(() => {
|
||||
component.simulate('mousedown');
|
||||
component.simulate("mousedown");
|
||||
});
|
||||
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('handling keyboard events', () => {
|
||||
it('calls onClick handler on enter keydown', () => {
|
||||
describe("handling keyboard events", () => {
|
||||
it("calls onClick handler on enter keydown", () => {
|
||||
const onClick = jest.fn();
|
||||
const component = getComponent({
|
||||
onClick,
|
||||
@@ -121,13 +121,13 @@ describe('<AccessibleButton />', () => {
|
||||
|
||||
const keyboardEvent = makeKeyboardEvent(Key.ENTER);
|
||||
act(() => {
|
||||
component.simulate('keydown', keyboardEvent);
|
||||
component.simulate("keydown", keyboardEvent);
|
||||
});
|
||||
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
|
||||
act(() => {
|
||||
component.simulate('keyup', keyboardEvent);
|
||||
component.simulate("keyup", keyboardEvent);
|
||||
});
|
||||
|
||||
// handler only called once on keydown
|
||||
@@ -137,7 +137,7 @@ describe('<AccessibleButton />', () => {
|
||||
expect(keyboardEvent.preventDefault).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('calls onClick handler on space keyup', () => {
|
||||
it("calls onClick handler on space keyup", () => {
|
||||
const onClick = jest.fn();
|
||||
const component = getComponent({
|
||||
onClick,
|
||||
@@ -145,13 +145,13 @@ describe('<AccessibleButton />', () => {
|
||||
|
||||
const keyboardEvent = makeKeyboardEvent(Key.SPACE);
|
||||
act(() => {
|
||||
component.simulate('keydown', keyboardEvent);
|
||||
component.simulate("keydown", keyboardEvent);
|
||||
});
|
||||
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
|
||||
act(() => {
|
||||
component.simulate('keyup', keyboardEvent);
|
||||
component.simulate("keyup", keyboardEvent);
|
||||
});
|
||||
|
||||
// handler only called once on keyup
|
||||
@@ -161,7 +161,7 @@ describe('<AccessibleButton />', () => {
|
||||
expect(keyboardEvent.preventDefault).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('calls onKeydown/onKeyUp handlers for keys other than space and enter', () => {
|
||||
it("calls onKeydown/onKeyUp handlers for keys other than space and enter", () => {
|
||||
const onClick = jest.fn();
|
||||
const onKeyDown = jest.fn();
|
||||
const onKeyUp = jest.fn();
|
||||
@@ -173,8 +173,8 @@ describe('<AccessibleButton />', () => {
|
||||
|
||||
const keyboardEvent = makeKeyboardEvent(Key.K);
|
||||
act(() => {
|
||||
component.simulate('keydown', keyboardEvent);
|
||||
component.simulate('keyup', keyboardEvent);
|
||||
component.simulate("keydown", keyboardEvent);
|
||||
component.simulate("keyup", keyboardEvent);
|
||||
});
|
||||
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
@@ -184,7 +184,7 @@ describe('<AccessibleButton />', () => {
|
||||
expect(keyboardEvent.preventDefault).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does nothing on non space/enter key presses when no onKeydown/onKeyUp handlers provided', () => {
|
||||
it("does nothing on non space/enter key presses when no onKeydown/onKeyUp handlers provided", () => {
|
||||
const onClick = jest.fn();
|
||||
const component = getComponent({
|
||||
onClick,
|
||||
@@ -192,8 +192,8 @@ describe('<AccessibleButton />', () => {
|
||||
|
||||
const keyboardEvent = makeKeyboardEvent(Key.K);
|
||||
act(() => {
|
||||
component.simulate('keydown', keyboardEvent);
|
||||
component.simulate('keyup', keyboardEvent);
|
||||
component.simulate("keydown", keyboardEvent);
|
||||
component.simulate("keyup", keyboardEvent);
|
||||
});
|
||||
|
||||
// no onClick call, no problems
|
||||
|
||||
@@ -52,17 +52,15 @@ describe("AppTile", () => {
|
||||
let app1: IApp;
|
||||
let app2: IApp;
|
||||
|
||||
const waitForRps = (roomId: string) => new Promise<void>(resolve => {
|
||||
const update = () => {
|
||||
if (
|
||||
RightPanelStore.instance.currentCardForRoom(roomId).phase !==
|
||||
RightPanelPhases.Widget
|
||||
) return;
|
||||
RightPanelStore.instance.off(UPDATE_EVENT, update);
|
||||
resolve();
|
||||
};
|
||||
RightPanelStore.instance.on(UPDATE_EVENT, update);
|
||||
});
|
||||
const waitForRps = (roomId: string) =>
|
||||
new Promise<void>((resolve) => {
|
||||
const update = () => {
|
||||
if (RightPanelStore.instance.currentCardForRoom(roomId).phase !== RightPanelPhases.Widget) return;
|
||||
RightPanelStore.instance.off(UPDATE_EVENT, update);
|
||||
resolve();
|
||||
};
|
||||
RightPanelStore.instance.on(UPDATE_EVENT, update);
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
stubClient();
|
||||
@@ -75,7 +73,7 @@ describe("AppTile", () => {
|
||||
r1 = new Room("r1", cli, "@name:example.com");
|
||||
r2 = new Room("r2", cli, "@name:example.com");
|
||||
|
||||
jest.spyOn(cli, "getRoom").mockImplementation(roomId => {
|
||||
jest.spyOn(cli, "getRoom").mockImplementation((roomId) => {
|
||||
if (roomId === "r1") return r1;
|
||||
if (roomId === "r2") return r2;
|
||||
return null;
|
||||
@@ -105,7 +103,7 @@ describe("AppTile", () => {
|
||||
creatorUserId: cli.getUserId(),
|
||||
avatar_url: undefined,
|
||||
};
|
||||
jest.spyOn(WidgetStore.instance, "getApps").mockImplementation(roomId => {
|
||||
jest.spyOn(WidgetStore.instance, "getApps").mockImplementation((roomId) => {
|
||||
if (roomId === "r1") return [app1];
|
||||
if (roomId === "r2") return [app2];
|
||||
});
|
||||
@@ -130,12 +128,14 @@ describe("AppTile", () => {
|
||||
if (name !== "RightPanel.phases") return realGetValue(name, roomId);
|
||||
if (roomId === "r1") {
|
||||
return {
|
||||
history: [{
|
||||
phase: RightPanelPhases.Widget,
|
||||
state: {
|
||||
widgetId: "1",
|
||||
history: [
|
||||
{
|
||||
phase: RightPanelPhases.Widget,
|
||||
state: {
|
||||
widgetId: "1",
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
isOpen: true,
|
||||
};
|
||||
}
|
||||
@@ -143,12 +143,11 @@ describe("AppTile", () => {
|
||||
});
|
||||
|
||||
// Run initial render with room 1, and also running lifecycle methods
|
||||
const renderer = TestRenderer.create(<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel
|
||||
room={r1}
|
||||
resizeNotifier={resizeNotifier}
|
||||
/>
|
||||
</MatrixClientContext.Provider>);
|
||||
const renderer = TestRenderer.create(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel room={r1} resizeNotifier={resizeNotifier} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
// Wait for RPS room 1 updates to fire
|
||||
const rpsUpdated = waitForRps("r1");
|
||||
dis.dispatch({
|
||||
@@ -169,12 +168,11 @@ describe("AppTile", () => {
|
||||
action: Action.ViewRoom,
|
||||
room_id: "r2",
|
||||
});
|
||||
renderer.update(<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel
|
||||
room={r2}
|
||||
resizeNotifier={resizeNotifier}
|
||||
/>
|
||||
</MatrixClientContext.Provider>);
|
||||
renderer.update(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel room={r2} resizeNotifier={resizeNotifier} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
expect(endWidgetActions.mock.calls.length).toBe(1);
|
||||
expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(false);
|
||||
@@ -185,16 +183,18 @@ describe("AppTile", () => {
|
||||
it("distinguishes widgets with the same ID in different rooms", async () => {
|
||||
// Set up right panel state
|
||||
const realGetValue = SettingsStore.getValue;
|
||||
jest.spyOn(SettingsStore, 'getValue').mockImplementation((name, roomId) => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((name, roomId) => {
|
||||
if (name === "RightPanel.phases") {
|
||||
if (roomId === "r1") {
|
||||
return {
|
||||
history: [{
|
||||
phase: RightPanelPhases.Widget,
|
||||
state: {
|
||||
widgetId: "1",
|
||||
history: [
|
||||
{
|
||||
phase: RightPanelPhases.Widget,
|
||||
state: {
|
||||
widgetId: "1",
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
isOpen: true,
|
||||
};
|
||||
}
|
||||
@@ -204,12 +204,11 @@ describe("AppTile", () => {
|
||||
});
|
||||
|
||||
// Run initial render with room 1, and also running lifecycle methods
|
||||
const renderer = TestRenderer.create(<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel
|
||||
room={r1}
|
||||
resizeNotifier={resizeNotifier}
|
||||
/>
|
||||
</MatrixClientContext.Provider>);
|
||||
const renderer = TestRenderer.create(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel room={r1} resizeNotifier={resizeNotifier} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
// Wait for RPS room 1 updates to fire
|
||||
const rpsUpdated1 = waitForRps("r1");
|
||||
dis.dispatch({
|
||||
@@ -225,12 +224,14 @@ describe("AppTile", () => {
|
||||
if (name === "RightPanel.phases") {
|
||||
if (roomId === "r2") {
|
||||
return {
|
||||
history: [{
|
||||
phase: RightPanelPhases.Widget,
|
||||
state: {
|
||||
widgetId: "1",
|
||||
history: [
|
||||
{
|
||||
phase: RightPanelPhases.Widget,
|
||||
state: {
|
||||
widgetId: "1",
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
isOpen: true,
|
||||
};
|
||||
}
|
||||
@@ -245,12 +246,11 @@ describe("AppTile", () => {
|
||||
action: Action.ViewRoom,
|
||||
room_id: "r2",
|
||||
});
|
||||
renderer.update(<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel
|
||||
room={r2}
|
||||
resizeNotifier={resizeNotifier}
|
||||
/>
|
||||
</MatrixClientContext.Provider>);
|
||||
renderer.update(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel room={r2} resizeNotifier={resizeNotifier} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
await rpsUpdated2;
|
||||
|
||||
expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(false);
|
||||
@@ -279,13 +279,11 @@ describe("AppTile", () => {
|
||||
});
|
||||
|
||||
// Run initial render with room 1, and also running lifecycle methods
|
||||
const renderer = TestRenderer.create(<MatrixClientContext.Provider value={cli}>
|
||||
<AppsDrawer
|
||||
userId={cli.getUserId()}
|
||||
room={r1}
|
||||
resizeNotifier={resizeNotifier}
|
||||
/>
|
||||
</MatrixClientContext.Provider>);
|
||||
const renderer = TestRenderer.create(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<AppsDrawer userId={cli.getUserId()} room={r1} resizeNotifier={resizeNotifier} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(true);
|
||||
|
||||
@@ -319,38 +317,34 @@ describe("AppTile", () => {
|
||||
let moveToContainerSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount((
|
||||
wrapper = mount(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<AppTile
|
||||
key={app1.id}
|
||||
app={app1}
|
||||
room={r1}
|
||||
/>
|
||||
</MatrixClientContext.Provider>
|
||||
));
|
||||
<AppTile key={app1.id} app={app1} room={r1} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
moveToContainerSpy = jest.spyOn(WidgetLayoutStore.instance, 'moveToContainer');
|
||||
moveToContainerSpy = jest.spyOn(WidgetLayoutStore.instance, "moveToContainer");
|
||||
});
|
||||
|
||||
it("requiresClient should be true", () => {
|
||||
expect(wrapper.state('requiresClient')).toBe(true);
|
||||
expect(wrapper.state("requiresClient")).toBe(true);
|
||||
});
|
||||
|
||||
it("clicking 'minimise' should send the widget to the right", () => {
|
||||
const minimiseButton = wrapper.find('.mx_AppTileMenuBar_iconButton_minimise');
|
||||
minimiseButton.first().simulate('click');
|
||||
const minimiseButton = wrapper.find(".mx_AppTileMenuBar_iconButton_minimise");
|
||||
minimiseButton.first().simulate("click");
|
||||
expect(moveToContainerSpy).toHaveBeenCalledWith(r1, app1, Container.Right);
|
||||
});
|
||||
|
||||
it("clicking 'maximise' should send the widget to the center", () => {
|
||||
const minimiseButton = wrapper.find('.mx_AppTileMenuBar_iconButton_maximise');
|
||||
minimiseButton.first().simulate('click');
|
||||
const minimiseButton = wrapper.find(".mx_AppTileMenuBar_iconButton_maximise");
|
||||
minimiseButton.first().simulate("click");
|
||||
expect(moveToContainerSpy).toHaveBeenCalledWith(r1, app1, Container.Center);
|
||||
});
|
||||
|
||||
describe("for a maximised (centered) widget", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(WidgetLayoutStore.instance, 'isInContainer').mockImplementation(
|
||||
jest.spyOn(WidgetLayoutStore.instance, "isInContainer").mockImplementation(
|
||||
(room: Optional<Room>, widget: IApp, container: Container) => {
|
||||
return room === r1 && widget === app1 && container === Container.Center;
|
||||
},
|
||||
@@ -358,8 +352,8 @@ describe("AppTile", () => {
|
||||
});
|
||||
|
||||
it("clicking 'un-maximise' should send the widget to the top", () => {
|
||||
const unMaximiseButton = wrapper.find('.mx_AppTileMenuBar_iconButton_collapse');
|
||||
unMaximiseButton.first().simulate('click');
|
||||
const unMaximiseButton = wrapper.find(".mx_AppTileMenuBar_iconButton_collapse");
|
||||
unMaximiseButton.first().simulate("click");
|
||||
expect(moveToContainerSpy).toHaveBeenCalledWith(r1, app1, Container.Top);
|
||||
});
|
||||
});
|
||||
@@ -378,19 +372,15 @@ describe("AppTile", () => {
|
||||
const mockWidget = new ElementWidget(app1);
|
||||
WidgetMessagingStore.instance.storeMessaging(mockWidget, r1.roomId, api);
|
||||
|
||||
wrapper = mount((
|
||||
wrapper = mount(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<AppTile
|
||||
key={app1.id}
|
||||
app={app1}
|
||||
room={r1}
|
||||
/>
|
||||
</MatrixClientContext.Provider>
|
||||
));
|
||||
<AppTile key={app1.id} app={app1} room={r1} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
});
|
||||
|
||||
it("requiresClient should be false", () => {
|
||||
expect(wrapper.state('requiresClient')).toBe(false);
|
||||
expect(wrapper.state("requiresClient")).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,23 +14,23 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount, ReactWrapper } from 'enzyme';
|
||||
import { MatrixEvent, RoomMember } from 'matrix-js-sdk/src/matrix';
|
||||
import { mount, ReactWrapper } from "enzyme";
|
||||
import { MatrixEvent, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
mkMembership,
|
||||
mockClientMethodsUser,
|
||||
unmockClientPeg,
|
||||
} from '../../../test-utils';
|
||||
} from "../../../test-utils";
|
||||
import EventListSummary from "../../../../src/components/views/elements/EventListSummary";
|
||||
import { Layout } from '../../../../src/settings/enums/Layout';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import { Layout } from "../../../../src/settings/enums/Layout";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
|
||||
describe('EventListSummary', function() {
|
||||
const roomId = '!room:server.org';
|
||||
describe("EventListSummary", function () {
|
||||
const roomId = "!room:server.org";
|
||||
// Generate dummy event tiles for use in simulating an expanded MELS
|
||||
const generateTiles = (events: MatrixEvent[]) => {
|
||||
return events.map((e) => {
|
||||
@@ -64,13 +64,14 @@ describe('EventListSummary', function() {
|
||||
prevMembership?: string;
|
||||
}
|
||||
const generateMembershipEvent = (
|
||||
eventId: string, { senderId, userId, membership, prevMembership }: MembershipEventParams,
|
||||
eventId: string,
|
||||
{ senderId, userId, membership, prevMembership }: MembershipEventParams,
|
||||
): MatrixEvent => {
|
||||
const member = new RoomMember(roomId, userId);
|
||||
// Use localpart as display name;
|
||||
member.name = userId.match(/@([^:]*):/)[1];
|
||||
jest.spyOn(member, 'getAvatarUrl').mockReturnValue('avatar.jpeg');
|
||||
jest.spyOn(member, 'getMxcAvatarUrl').mockReturnValue('mxc://avatar.url/image.png');
|
||||
jest.spyOn(member, "getAvatarUrl").mockReturnValue("avatar.jpeg");
|
||||
jest.spyOn(member, "getMxcAvatarUrl").mockReturnValue("mxc://avatar.url/image.png");
|
||||
const e = mkMembership({
|
||||
event: true,
|
||||
room: roomId,
|
||||
@@ -102,7 +103,7 @@ describe('EventListSummary', function() {
|
||||
let eventsForUsers = [];
|
||||
let userId = "";
|
||||
for (let i = 0; i < n; i++) {
|
||||
userId = userIdTemplate.replace('$', i);
|
||||
userId = userIdTemplate.replace("$", i);
|
||||
events.forEach((e) => {
|
||||
e.userId = userId;
|
||||
});
|
||||
@@ -121,13 +122,14 @@ describe('EventListSummary', function() {
|
||||
children: [],
|
||||
};
|
||||
const renderComponent = (props = {}): ReactWrapper => {
|
||||
return mount(<MatrixClientContext.Provider value={mockClient}>
|
||||
<EventListSummary {...defaultProps} {...props} />
|
||||
</MatrixClientContext.Provider>,
|
||||
return mount(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<EventListSummary {...defaultProps} {...props} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -135,10 +137,8 @@ describe('EventListSummary', function() {
|
||||
unmockClientPeg();
|
||||
});
|
||||
|
||||
it('renders expanded events if there are less than props.threshold', function() {
|
||||
const events = generateEvents([
|
||||
{ userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
|
||||
]);
|
||||
it("renders expanded events if there are less than props.threshold", function () {
|
||||
const events = generateEvents([{ userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" }]);
|
||||
const props = {
|
||||
events: events,
|
||||
children: generateTiles(events),
|
||||
@@ -149,12 +149,14 @@ describe('EventListSummary', function() {
|
||||
|
||||
const wrapper = renderComponent(props); // matrix cli context wrapper
|
||||
|
||||
expect(wrapper.find('GenericEventListSummary').props().children).toEqual([
|
||||
<div className="event_tile" key="event0">Expanded membership</div>,
|
||||
expect(wrapper.find("GenericEventListSummary").props().children).toEqual([
|
||||
<div className="event_tile" key="event0">
|
||||
Expanded membership
|
||||
</div>,
|
||||
]);
|
||||
});
|
||||
|
||||
it('renders expanded events if there are less than props.threshold', function() {
|
||||
it("renders expanded events if there are less than props.threshold", function () {
|
||||
const events = generateEvents([
|
||||
{ userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
|
||||
{ userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
|
||||
@@ -169,13 +171,17 @@ describe('EventListSummary', function() {
|
||||
|
||||
const wrapper = renderComponent(props); // matrix cli context wrapper
|
||||
|
||||
expect(wrapper.find('GenericEventListSummary').props().children).toEqual([
|
||||
<div className="event_tile" key="event0">Expanded membership</div>,
|
||||
<div className="event_tile" key="event1">Expanded membership</div>,
|
||||
expect(wrapper.find("GenericEventListSummary").props().children).toEqual([
|
||||
<div className="event_tile" key="event0">
|
||||
Expanded membership
|
||||
</div>,
|
||||
<div className="event_tile" key="event1">
|
||||
Expanded membership
|
||||
</div>,
|
||||
]);
|
||||
});
|
||||
|
||||
it('renders collapsed events if events.length = props.threshold', function() {
|
||||
it("renders collapsed events if events.length = props.threshold", function () {
|
||||
const events = generateEvents([
|
||||
{ userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
|
||||
{ userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
|
||||
@@ -196,7 +202,7 @@ describe('EventListSummary', function() {
|
||||
expect(summaryText).toBe("user_1 joined and left and joined");
|
||||
});
|
||||
|
||||
it('truncates long join,leave repetitions', function() {
|
||||
it("truncates long join,leave repetitions", function () {
|
||||
const events = generateEvents([
|
||||
{ userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
|
||||
{ userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
|
||||
@@ -228,7 +234,7 @@ describe('EventListSummary', function() {
|
||||
expect(summaryText).toBe("user_1 joined and left 7 times");
|
||||
});
|
||||
|
||||
it('truncates long join,leave repetitions between other events', function() {
|
||||
it("truncates long join,leave repetitions between other events", function () {
|
||||
const events = generateEvents([
|
||||
{
|
||||
userId: "@user_1:some.domain",
|
||||
@@ -269,12 +275,10 @@ describe('EventListSummary', function() {
|
||||
const summary = wrapper.find(".mx_GenericEventListSummary_summary");
|
||||
const summaryText = summary.text();
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_1 was unbanned, joined and left 7 times and was invited",
|
||||
);
|
||||
expect(summaryText).toBe("user_1 was unbanned, joined and left 7 times and was invited");
|
||||
});
|
||||
|
||||
it('truncates multiple sequences of repetitions with other events between', function() {
|
||||
it("truncates multiple sequences of repetitions with other events between", function () {
|
||||
const events = generateEvents([
|
||||
{
|
||||
userId: "@user_1:some.domain",
|
||||
@@ -318,12 +322,11 @@ describe('EventListSummary', function() {
|
||||
const summaryText = summary.text();
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_1 was unbanned, joined and left 2 times, was banned, " +
|
||||
"joined and left 3 times and was invited",
|
||||
"user_1 was unbanned, joined and left 2 times, was banned, " + "joined and left 3 times and was invited",
|
||||
);
|
||||
});
|
||||
|
||||
it('handles multiple users following the same sequence of memberships', function() {
|
||||
it("handles multiple users following the same sequence of memberships", function () {
|
||||
const events = generateEvents([
|
||||
// user_1
|
||||
{
|
||||
@@ -372,12 +375,10 @@ describe('EventListSummary', function() {
|
||||
const summary = wrapper.find(".mx_GenericEventListSummary_summary");
|
||||
const summaryText = summary.text();
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_1 and one other were unbanned, joined and left 2 times and were banned",
|
||||
);
|
||||
expect(summaryText).toBe("user_1 and one other were unbanned, joined and left 2 times and were banned");
|
||||
});
|
||||
|
||||
it('handles many users following the same sequence of memberships', function() {
|
||||
it("handles many users following the same sequence of memberships", function () {
|
||||
const events = generateEventsForUsers("@user_$:some.domain", 20, [
|
||||
{
|
||||
prevMembership: "ban",
|
||||
@@ -406,12 +407,10 @@ describe('EventListSummary', function() {
|
||||
const summary = wrapper.find(".mx_GenericEventListSummary_summary");
|
||||
const summaryText = summary.text();
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_0 and 19 others were unbanned, joined and left 2 times and were banned",
|
||||
);
|
||||
expect(summaryText).toBe("user_0 and 19 others were unbanned, joined and left 2 times and were banned");
|
||||
});
|
||||
|
||||
it('correctly orders sequences of transitions by the order of their first event', function() {
|
||||
it("correctly orders sequences of transitions by the order of their first event", function () {
|
||||
const events = generateEvents([
|
||||
{
|
||||
userId: "@user_2:some.domain",
|
||||
@@ -454,11 +453,11 @@ describe('EventListSummary', function() {
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_2 was unbanned and joined and left 2 times, user_1 was unbanned, " +
|
||||
"joined and left 2 times and was banned",
|
||||
"joined and left 2 times and was banned",
|
||||
);
|
||||
});
|
||||
|
||||
it('correctly identifies transitions', function() {
|
||||
it("correctly identifies transitions", function () {
|
||||
const events = generateEvents([
|
||||
// invited
|
||||
{ userId: "@user_1:some.domain", membership: "invite" },
|
||||
@@ -524,11 +523,11 @@ describe('EventListSummary', function() {
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_1 was invited, was banned, joined, rejected their invitation, left, " +
|
||||
"had their invitation withdrawn, was unbanned, was removed, left and was removed",
|
||||
"had their invitation withdrawn, was unbanned, was removed, left and was removed",
|
||||
);
|
||||
});
|
||||
|
||||
it('handles invitation plurals correctly when there are multiple users', function() {
|
||||
it("handles invitation plurals correctly when there are multiple users", function () {
|
||||
const events = generateEvents([
|
||||
{
|
||||
userId: "@user_1:some.domain",
|
||||
@@ -566,12 +565,11 @@ describe('EventListSummary', function() {
|
||||
const summaryText = summary.text();
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_1 and one other rejected their invitations and " +
|
||||
"had their invitations withdrawn",
|
||||
"user_1 and one other rejected their invitations and " + "had their invitations withdrawn",
|
||||
);
|
||||
});
|
||||
|
||||
it('handles invitation plurals correctly when there are multiple invites', function() {
|
||||
it("handles invitation plurals correctly when there are multiple invites", function () {
|
||||
const events = generateEvents([
|
||||
{
|
||||
userId: "@user_1:some.domain",
|
||||
@@ -596,12 +594,10 @@ describe('EventListSummary', function() {
|
||||
const summary = wrapper.find(".mx_GenericEventListSummary_summary");
|
||||
const summaryText = summary.text();
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_1 rejected their invitation 2 times",
|
||||
);
|
||||
expect(summaryText).toBe("user_1 rejected their invitation 2 times");
|
||||
});
|
||||
|
||||
it('handles a summary length = 2, with no "others"', function() {
|
||||
it('handles a summary length = 2, with no "others"', function () {
|
||||
const events = generateEvents([
|
||||
{ userId: "@user_1:some.domain", membership: "join" },
|
||||
{ userId: "@user_1:some.domain", membership: "join" },
|
||||
@@ -620,12 +616,10 @@ describe('EventListSummary', function() {
|
||||
const summary = wrapper.find(".mx_GenericEventListSummary_summary");
|
||||
const summaryText = summary.text();
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_1 and user_2 joined 2 times",
|
||||
);
|
||||
expect(summaryText).toBe("user_1 and user_2 joined 2 times");
|
||||
});
|
||||
|
||||
it('handles a summary length = 2, with 1 "other"', function() {
|
||||
it('handles a summary length = 2, with 1 "other"', function () {
|
||||
const events = generateEvents([
|
||||
{ userId: "@user_1:some.domain", membership: "join" },
|
||||
{ userId: "@user_2:some.domain", membership: "join" },
|
||||
@@ -643,15 +637,11 @@ describe('EventListSummary', function() {
|
||||
const summary = wrapper.find(".mx_GenericEventListSummary_summary");
|
||||
const summaryText = summary.text();
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_1, user_2 and one other joined",
|
||||
);
|
||||
expect(summaryText).toBe("user_1, user_2 and one other joined");
|
||||
});
|
||||
|
||||
it('handles a summary length = 2, with many "others"', function() {
|
||||
const events = generateEventsForUsers("@user_$:some.domain", 20, [
|
||||
{ membership: "join" },
|
||||
]);
|
||||
it('handles a summary length = 2, with many "others"', function () {
|
||||
const events = generateEventsForUsers("@user_$:some.domain", 20, [{ membership: "join" }]);
|
||||
const props = {
|
||||
events: events,
|
||||
children: generateTiles(events),
|
||||
@@ -664,8 +654,6 @@ describe('EventListSummary', function() {
|
||||
const summary = wrapper.find(".mx_GenericEventListSummary_summary");
|
||||
const summaryText = summary.text();
|
||||
|
||||
expect(summaryText).toBe(
|
||||
"user_0, user_1 and 18 others joined",
|
||||
);
|
||||
expect(summaryText).toBe("user_0, user_1 and 18 others joined");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,39 +12,45 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { renderIntoDocument } from 'react-dom/test-utils';
|
||||
import React from "react";
|
||||
import { renderIntoDocument } from "react-dom/test-utils";
|
||||
|
||||
import ExternalLink from '../../../../src/components/views/elements/ExternalLink';
|
||||
import ExternalLink from "../../../../src/components/views/elements/ExternalLink";
|
||||
|
||||
describe('<ExternalLink />', () => {
|
||||
describe("<ExternalLink />", () => {
|
||||
const defaultProps = {
|
||||
"href": 'test.com',
|
||||
"href": "test.com",
|
||||
"onClick": jest.fn(),
|
||||
"className": 'myCustomClass',
|
||||
'data-test-id': 'test',
|
||||
"className": "myCustomClass",
|
||||
"data-test-id": "test",
|
||||
};
|
||||
const getComponent = (props = {}) => {
|
||||
const wrapper = renderIntoDocument<HTMLDivElement>(
|
||||
<div><ExternalLink {...defaultProps} {...props} /></div>,
|
||||
<div>
|
||||
<ExternalLink {...defaultProps} {...props} />
|
||||
</div>,
|
||||
) as HTMLDivElement;
|
||||
return wrapper.children[0];
|
||||
};
|
||||
|
||||
it('renders link correctly', () => {
|
||||
const children = <span>react element <b>children</b></span>;
|
||||
expect(getComponent({ children, target: '_self', rel: 'noopener' })).toMatchSnapshot();
|
||||
it("renders link correctly", () => {
|
||||
const children = (
|
||||
<span>
|
||||
react element <b>children</b>
|
||||
</span>
|
||||
);
|
||||
expect(getComponent({ children, target: "_self", rel: "noopener" })).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('defaults target and rel', () => {
|
||||
const children = 'test';
|
||||
it("defaults target and rel", () => {
|
||||
const children = "test";
|
||||
const component = getComponent({ children });
|
||||
expect(component.getAttribute('rel')).toEqual('noreferrer noopener');
|
||||
expect(component.getAttribute('target')).toEqual('_blank');
|
||||
expect(component.getAttribute("rel")).toEqual("noreferrer noopener");
|
||||
expect(component.getAttribute("target")).toEqual("_blank");
|
||||
});
|
||||
|
||||
it('renders plain text link correctly', () => {
|
||||
const children = 'test';
|
||||
it("renders plain text link correctly", () => {
|
||||
const children = "test";
|
||||
expect(getComponent({ children })).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,55 +14,55 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { act, fireEvent, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { act, fireEvent, render } from "@testing-library/react";
|
||||
import React from "react";
|
||||
|
||||
import { FilterDropdown } from '../../../../src/components/views/elements/FilterDropdown';
|
||||
import { flushPromises, mockPlatformPeg } from '../../../test-utils';
|
||||
import { FilterDropdown } from "../../../../src/components/views/elements/FilterDropdown";
|
||||
import { flushPromises, mockPlatformPeg } from "../../../test-utils";
|
||||
|
||||
mockPlatformPeg();
|
||||
|
||||
describe('<FilterDropdown />', () => {
|
||||
describe("<FilterDropdown />", () => {
|
||||
const options = [
|
||||
{ id: 'one', label: 'Option one' },
|
||||
{ id: 'two', label: 'Option two', description: 'with description' },
|
||||
{ id: "one", label: "Option one" },
|
||||
{ id: "two", label: "Option two", description: "with description" },
|
||||
];
|
||||
const defaultProps = {
|
||||
className: 'test',
|
||||
value: 'one',
|
||||
className: "test",
|
||||
value: "one",
|
||||
options,
|
||||
id: 'test',
|
||||
label: 'test label',
|
||||
id: "test",
|
||||
label: "test label",
|
||||
onOptionChange: jest.fn(),
|
||||
};
|
||||
const getComponent = (props = {}): JSX.Element =>
|
||||
(<FilterDropdown {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}): JSX.Element => <FilterDropdown {...defaultProps} {...props} />;
|
||||
|
||||
const openDropdown = async (container: HTMLElement): Promise<void> => await act(async () => {
|
||||
const button = container.querySelector('[role="button"]');
|
||||
expect(button).toBeTruthy();
|
||||
fireEvent.click(button as Element);
|
||||
await flushPromises();
|
||||
});
|
||||
const openDropdown = async (container: HTMLElement): Promise<void> =>
|
||||
await act(async () => {
|
||||
const button = container.querySelector('[role="button"]');
|
||||
expect(button).toBeTruthy();
|
||||
fireEvent.click(button as Element);
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
it('renders selected option', () => {
|
||||
it("renders selected option", () => {
|
||||
const { container } = render(getComponent());
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders when selected option is not in options', () => {
|
||||
const { container } = render(getComponent({ value: 'oops' }));
|
||||
it("renders when selected option is not in options", () => {
|
||||
const { container } = render(getComponent({ value: "oops" }));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders selected option with selectedLabel', () => {
|
||||
const { container } = render(getComponent({ selectedLabel: 'Show' }));
|
||||
it("renders selected option with selectedLabel", () => {
|
||||
const { container } = render(getComponent({ selectedLabel: "Show" }));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders dropdown options in menu', async () => {
|
||||
it("renders dropdown options in menu", async () => {
|
||||
const { container } = render(getComponent());
|
||||
await openDropdown(container);
|
||||
expect(container.querySelector('.mx_Dropdown_menu')).toMatchSnapshot();
|
||||
expect(container.querySelector(".mx_Dropdown_menu")).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,24 +14,21 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { mount } from "enzyme";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import LabelledCheckbox from "../../../../src/components/views/elements/LabelledCheckbox";
|
||||
|
||||
// Fake random strings to give a predictable snapshot for checkbox IDs
|
||||
jest.mock(
|
||||
'matrix-js-sdk/src/randomstring',
|
||||
() => {
|
||||
return {
|
||||
randomString: () => "abdefghi",
|
||||
};
|
||||
},
|
||||
);
|
||||
jest.mock("matrix-js-sdk/src/randomstring", () => {
|
||||
return {
|
||||
randomString: () => "abdefghi",
|
||||
};
|
||||
});
|
||||
|
||||
describe('<LabelledCheckbox />', () => {
|
||||
describe("<LabelledCheckbox />", () => {
|
||||
type CompProps = React.ComponentProps<typeof LabelledCheckbox>;
|
||||
const getComponent = (props: CompProps) => mount(<LabelledCheckbox {...props} />);
|
||||
type CompClass = ReturnType<typeof getComponent>;
|
||||
@@ -42,29 +39,26 @@ describe('<LabelledCheckbox />', () => {
|
||||
|
||||
const isChecked = (checkbox: ReturnType<typeof getCheckbox>) => checkbox.is(`[checked=true]`);
|
||||
const isDisabled = (checkbox: ReturnType<typeof getCheckbox>) => checkbox.is(`[disabled=true]`);
|
||||
const getText = (span: ReturnType<typeof getLabel>) => span.length > 0 ? span.at(0).text() : null;
|
||||
const getText = (span: ReturnType<typeof getLabel>) => (span.length > 0 ? span.at(0).text() : null);
|
||||
|
||||
test.each([null, "this is a byline"])(
|
||||
"should render with byline of %p",
|
||||
(byline) => {
|
||||
const props: CompProps = {
|
||||
label: "Hello world",
|
||||
value: true,
|
||||
byline: byline,
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
const component = getComponent(props);
|
||||
const checkbox = getCheckbox(component);
|
||||
test.each([null, "this is a byline"])("should render with byline of %p", (byline) => {
|
||||
const props: CompProps = {
|
||||
label: "Hello world",
|
||||
value: true,
|
||||
byline: byline,
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
const component = getComponent(props);
|
||||
const checkbox = getCheckbox(component);
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(isChecked(checkbox)).toBe(true);
|
||||
expect(isDisabled(checkbox)).toBe(false);
|
||||
expect(getText(getLabel(component))).toBe(props.label);
|
||||
expect(getText(getByline(component))).toBe(byline);
|
||||
},
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(isChecked(checkbox)).toBe(true);
|
||||
expect(isDisabled(checkbox)).toBe(false);
|
||||
expect(getText(getLabel(component))).toBe(props.label);
|
||||
expect(getText(getByline(component))).toBe(byline);
|
||||
});
|
||||
|
||||
it('should support unchecked by default', () => {
|
||||
it("should support unchecked by default", () => {
|
||||
const props: CompProps = {
|
||||
label: "Hello world",
|
||||
value: false,
|
||||
@@ -75,7 +69,7 @@ describe('<LabelledCheckbox />', () => {
|
||||
expect(isChecked(getCheckbox(component))).toBe(false);
|
||||
});
|
||||
|
||||
it('should be possible to disable the checkbox', () => {
|
||||
it("should be possible to disable the checkbox", () => {
|
||||
const props: CompProps = {
|
||||
label: "Hello world",
|
||||
value: false,
|
||||
@@ -87,7 +81,7 @@ describe('<LabelledCheckbox />', () => {
|
||||
expect(isDisabled(getCheckbox(component))).toBe(true);
|
||||
});
|
||||
|
||||
it('should emit onChange calls', () => {
|
||||
it("should emit onChange calls", () => {
|
||||
const props: CompProps = {
|
||||
label: "Hello world",
|
||||
value: false,
|
||||
@@ -98,13 +92,13 @@ describe('<LabelledCheckbox />', () => {
|
||||
expect(props.onChange).not.toHaveBeenCalled();
|
||||
|
||||
act(() => {
|
||||
getCheckbox(component).simulate('change');
|
||||
getCheckbox(component).simulate("change");
|
||||
});
|
||||
|
||||
expect(props.onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should react to value and disabled prop changes', () => {
|
||||
it("should react to value and disabled prop changes", () => {
|
||||
const props: CompProps = {
|
||||
label: "Hello world",
|
||||
value: false,
|
||||
|
||||
@@ -14,44 +14,41 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import React from "react";
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
|
||||
import LearnMore from '../../../../src/components/views/elements/LearnMore';
|
||||
import Modal from '../../../../src/Modal';
|
||||
import InfoDialog from '../../../../src/components/views/dialogs/InfoDialog';
|
||||
import LearnMore from "../../../../src/components/views/elements/LearnMore";
|
||||
import Modal from "../../../../src/Modal";
|
||||
import InfoDialog from "../../../../src/components/views/dialogs/InfoDialog";
|
||||
|
||||
describe('<LearnMore />', () => {
|
||||
describe("<LearnMore />", () => {
|
||||
const defaultProps = {
|
||||
title: 'Test',
|
||||
description: 'test test test',
|
||||
['data-testid']: 'testid',
|
||||
title: "Test",
|
||||
description: "test test test",
|
||||
["data-testid"]: "testid",
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
(<LearnMore {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => <LearnMore {...defaultProps} {...props} />;
|
||||
|
||||
const modalSpy = jest.spyOn(Modal, 'createDialog').mockReturnValue(undefined);
|
||||
const modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue(undefined);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders button', () => {
|
||||
it("renders button", () => {
|
||||
const { container } = render(getComponent());
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('opens modal on click', async () => {
|
||||
it("opens modal on click", async () => {
|
||||
const { getByTestId } = render(getComponent());
|
||||
fireEvent.click(getByTestId('testid'));
|
||||
fireEvent.click(getByTestId("testid"));
|
||||
|
||||
expect(modalSpy).toHaveBeenCalledWith(
|
||||
InfoDialog,
|
||||
{
|
||||
button: 'Got it',
|
||||
description: defaultProps.description,
|
||||
hasCloseButton: true,
|
||||
title: defaultProps.title,
|
||||
});
|
||||
expect(modalSpy).toHaveBeenCalledWith(InfoDialog, {
|
||||
button: "Got it",
|
||||
description: defaultProps.description,
|
||||
hasCloseButton: true,
|
||||
title: defaultProps.title,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,34 +19,28 @@ import { Linkify } from "../../../../src/components/views/elements/Linkify";
|
||||
|
||||
describe("Linkify", () => {
|
||||
it("linkifies the context", () => {
|
||||
const { container } = render(<Linkify>
|
||||
https://perdu.com
|
||||
</Linkify>);
|
||||
const { container } = render(<Linkify>https://perdu.com</Linkify>);
|
||||
expect(container.innerHTML).toBe(
|
||||
"<div><a href=\"https://perdu.com\" class=\"linkified\" target=\"_blank\" rel=\"noreferrer noopener\">"+
|
||||
"https://perdu.com" +
|
||||
"</a></div>",
|
||||
'<div><a href="https://perdu.com" class="linkified" target="_blank" rel="noreferrer noopener">' +
|
||||
"https://perdu.com" +
|
||||
"</a></div>",
|
||||
);
|
||||
});
|
||||
|
||||
it("correctly linkifies a room alias", () => {
|
||||
const { container } = render(<Linkify>
|
||||
#element-web:matrix.org
|
||||
</Linkify>);
|
||||
const { container } = render(<Linkify>#element-web:matrix.org</Linkify>);
|
||||
expect(container.innerHTML).toBe(
|
||||
"<div>" +
|
||||
"<a href=\"https://matrix.to/#/#element-web:matrix.org\" class=\"linkified\" rel=\"noreferrer noopener\">" +
|
||||
"#element-web:matrix.org" +
|
||||
"</a></div>",
|
||||
'<a href="https://matrix.to/#/#element-web:matrix.org" class="linkified" rel="noreferrer noopener">' +
|
||||
"#element-web:matrix.org" +
|
||||
"</a></div>",
|
||||
);
|
||||
});
|
||||
|
||||
it("changes the root tag name", () => {
|
||||
const TAG_NAME = "p";
|
||||
|
||||
const { container } = render(<Linkify as={TAG_NAME}>
|
||||
Hello world!
|
||||
</Linkify>);
|
||||
const { container } = render(<Linkify as={TAG_NAME}>Hello world!</Linkify>);
|
||||
|
||||
expect(container.querySelectorAll("p")).toHaveLength(1);
|
||||
});
|
||||
@@ -60,31 +54,29 @@ describe("Linkify", () => {
|
||||
|
||||
// upon clicking the element, change the content, and expect
|
||||
// linkify to update
|
||||
return <div onClick={onClick}>
|
||||
<Linkify>
|
||||
{ n % 2 === 0
|
||||
? "https://perdu.com"
|
||||
: "https://matrix.org" }
|
||||
</Linkify>
|
||||
</div>;
|
||||
return (
|
||||
<div onClick={onClick}>
|
||||
<Linkify>{n % 2 === 0 ? "https://perdu.com" : "https://matrix.org"}</Linkify>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { container } = render(<DummyTest />);
|
||||
|
||||
expect(container.innerHTML).toBe(
|
||||
"<div><div>" +
|
||||
"<a href=\"https://perdu.com\" class=\"linkified\" target=\"_blank\" rel=\"noreferrer noopener\">" +
|
||||
"https://perdu.com" +
|
||||
"</a></div></div>",
|
||||
'<a href="https://perdu.com" class="linkified" target="_blank" rel="noreferrer noopener">' +
|
||||
"https://perdu.com" +
|
||||
"</a></div></div>",
|
||||
);
|
||||
|
||||
fireEvent.click(container.querySelector("div"));
|
||||
|
||||
expect(container.innerHTML).toBe(
|
||||
"<div><div>" +
|
||||
"<a href=\"https://matrix.org\" class=\"linkified\" target=\"_blank\" rel=\"noreferrer noopener\">" +
|
||||
"https://matrix.org" +
|
||||
"</a></div></div>",
|
||||
'<a href="https://matrix.org" class="linkified" target="_blank" rel="noreferrer noopener">' +
|
||||
"https://matrix.org" +
|
||||
"</a></div></div>",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,16 +24,13 @@ import {
|
||||
M_POLL_START,
|
||||
M_TEXT,
|
||||
PollStartEvent,
|
||||
} from 'matrix-events-sdk';
|
||||
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||
} from "matrix-events-sdk";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
|
||||
import {
|
||||
findById,
|
||||
getMockClientWithEventEmitter,
|
||||
} from '../../../test-utils';
|
||||
import { findById, getMockClientWithEventEmitter } from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import PollCreateDialog from "../../../../src/components/views/elements/PollCreateDialog";
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
|
||||
// Fake date to give a predictable snapshot
|
||||
const realDateNow = Date.now;
|
||||
@@ -50,7 +47,7 @@ afterAll(() => {
|
||||
|
||||
describe("PollCreateDialog", () => {
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
sendEvent: jest.fn().mockResolvedValue({ event_id: '1' }),
|
||||
sendEvent: jest.fn().mockResolvedValue({ event_id: "1" }),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -58,48 +55,35 @@ describe("PollCreateDialog", () => {
|
||||
});
|
||||
|
||||
it("renders a blank poll", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
{
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: { value: mockClient },
|
||||
},
|
||||
);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />, {
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: { value: mockClient },
|
||||
});
|
||||
expect(dialog.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("autofocuses the poll topic on mount", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
expect(findById(dialog, 'poll-topic-input').at(0).props().autoFocus).toEqual(true);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
expect(findById(dialog, "poll-topic-input").at(0).props().autoFocus).toEqual(true);
|
||||
});
|
||||
|
||||
it("autofocuses the new poll option field after clicking add option button", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
expect(findById(dialog, 'poll-topic-input').at(0).props().autoFocus).toEqual(true);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
expect(findById(dialog, "poll-topic-input").at(0).props().autoFocus).toEqual(true);
|
||||
|
||||
dialog.find("div.mx_PollCreateDialog_addOption").simulate("click");
|
||||
|
||||
expect(findById(dialog, 'poll-topic-input').at(0).props().autoFocus).toEqual(false);
|
||||
expect(findById(dialog, 'pollcreate_option_1').at(0).props().autoFocus).toEqual(false);
|
||||
expect(findById(dialog, 'pollcreate_option_2').at(0).props().autoFocus).toEqual(true);
|
||||
expect(findById(dialog, "poll-topic-input").at(0).props().autoFocus).toEqual(false);
|
||||
expect(findById(dialog, "pollcreate_option_1").at(0).props().autoFocus).toEqual(false);
|
||||
expect(findById(dialog, "pollcreate_option_2").at(0).props().autoFocus).toEqual(true);
|
||||
});
|
||||
|
||||
it("renders a question and some options", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
expect(submitIsDisabled(dialog)).toBe(true);
|
||||
|
||||
// When I set some values in the boxes
|
||||
changeValue(
|
||||
dialog,
|
||||
"Question or topic",
|
||||
"How many turnips is the optimal number?",
|
||||
);
|
||||
changeValue(dialog, "Question or topic", "How many turnips is the optimal number?");
|
||||
changeValue(dialog, "Option 1", "As many as my neighbour");
|
||||
changeValue(dialog, "Option 2", "The question is meaningless");
|
||||
dialog.find("div.mx_PollCreateDialog_addOption").simulate("click");
|
||||
@@ -109,19 +93,11 @@ describe("PollCreateDialog", () => {
|
||||
|
||||
it("renders info from a previous event", () => {
|
||||
const previousEvent: MatrixEvent = new MatrixEvent(
|
||||
PollStartEvent.from(
|
||||
"Poll Q",
|
||||
["Answer 1", "Answer 2"],
|
||||
M_POLL_KIND_DISCLOSED,
|
||||
).serialize(),
|
||||
PollStartEvent.from("Poll Q", ["Answer 1", "Answer 2"], M_POLL_KIND_DISCLOSED).serialize(),
|
||||
);
|
||||
|
||||
const dialog = mount(
|
||||
<PollCreateDialog
|
||||
room={createRoom()}
|
||||
onFinished={jest.fn()}
|
||||
editingMxEvent={previousEvent}
|
||||
/>,
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} editingMxEvent={previousEvent} />,
|
||||
);
|
||||
|
||||
expect(submitIsDisabled(dialog)).toBe(false);
|
||||
@@ -129,17 +105,13 @@ describe("PollCreateDialog", () => {
|
||||
});
|
||||
|
||||
it("doesn't allow submitting until there are options", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
expect(submitIsDisabled(dialog)).toBe(true);
|
||||
});
|
||||
|
||||
it("does allow submitting when there are options and a question", () => {
|
||||
// Given a dialog with no info in (which I am unable to submit)
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
expect(submitIsDisabled(dialog)).toBe(true);
|
||||
|
||||
// When I set some values in the boxes
|
||||
@@ -152,74 +124,42 @@ describe("PollCreateDialog", () => {
|
||||
});
|
||||
|
||||
it("shows the open poll description at first", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
expect(
|
||||
dialog.find('select').prop("value"),
|
||||
).toEqual(M_POLL_KIND_DISCLOSED.name);
|
||||
expect(
|
||||
dialog.find('p').text(),
|
||||
).toEqual("Voters see results as soon as they have voted");
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
expect(dialog.find("select").prop("value")).toEqual(M_POLL_KIND_DISCLOSED.name);
|
||||
expect(dialog.find("p").text()).toEqual("Voters see results as soon as they have voted");
|
||||
});
|
||||
|
||||
it("shows the closed poll description if we choose it", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
changeKind(dialog, M_POLL_KIND_UNDISCLOSED.name);
|
||||
expect(
|
||||
dialog.find('select').prop("value"),
|
||||
).toEqual(M_POLL_KIND_UNDISCLOSED.name);
|
||||
expect(
|
||||
dialog.find('p').text(),
|
||||
).toEqual("Results are only revealed when you end the poll");
|
||||
expect(dialog.find("select").prop("value")).toEqual(M_POLL_KIND_UNDISCLOSED.name);
|
||||
expect(dialog.find("p").text()).toEqual("Results are only revealed when you end the poll");
|
||||
});
|
||||
|
||||
it("shows the open poll description if we choose it", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
changeKind(dialog, M_POLL_KIND_UNDISCLOSED.name);
|
||||
changeKind(dialog, M_POLL_KIND_DISCLOSED.name);
|
||||
expect(
|
||||
dialog.find('select').prop("value"),
|
||||
).toEqual(M_POLL_KIND_DISCLOSED.name);
|
||||
expect(
|
||||
dialog.find('p').text(),
|
||||
).toEqual("Voters see results as soon as they have voted");
|
||||
expect(dialog.find("select").prop("value")).toEqual(M_POLL_KIND_DISCLOSED.name);
|
||||
expect(dialog.find("p").text()).toEqual("Voters see results as soon as they have voted");
|
||||
});
|
||||
|
||||
it("shows the closed poll description when editing a closed poll", () => {
|
||||
const previousEvent: MatrixEvent = new MatrixEvent(
|
||||
PollStartEvent.from(
|
||||
"Poll Q",
|
||||
["Answer 1", "Answer 2"],
|
||||
M_POLL_KIND_UNDISCLOSED,
|
||||
).serialize(),
|
||||
PollStartEvent.from("Poll Q", ["Answer 1", "Answer 2"], M_POLL_KIND_UNDISCLOSED).serialize(),
|
||||
);
|
||||
previousEvent.event.event_id = "$prevEventId";
|
||||
|
||||
const dialog = mount(
|
||||
<PollCreateDialog
|
||||
room={createRoom()}
|
||||
onFinished={jest.fn()}
|
||||
editingMxEvent={previousEvent}
|
||||
/>,
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} editingMxEvent={previousEvent} />,
|
||||
);
|
||||
|
||||
expect(
|
||||
dialog.find('select').prop("value"),
|
||||
).toEqual(M_POLL_KIND_UNDISCLOSED.name);
|
||||
expect(
|
||||
dialog.find('p').text(),
|
||||
).toEqual("Results are only revealed when you end the poll");
|
||||
expect(dialog.find("select").prop("value")).toEqual(M_POLL_KIND_UNDISCLOSED.name);
|
||||
expect(dialog.find("p").text()).toEqual("Results are only revealed when you end the poll");
|
||||
});
|
||||
|
||||
it("displays a spinner after submitting", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
changeValue(dialog, "Question or topic", "Q");
|
||||
changeValue(dialog, "Option 1", "A1");
|
||||
changeValue(dialog, "Option 2", "A2");
|
||||
@@ -230,9 +170,7 @@ describe("PollCreateDialog", () => {
|
||||
});
|
||||
|
||||
it("sends a poll create event when submitted", () => {
|
||||
const dialog = mount(
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||
);
|
||||
const dialog = mount(<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />);
|
||||
changeValue(dialog, "Question or topic", "Q");
|
||||
changeValue(dialog, "Option 1", "A1");
|
||||
changeValue(dialog, "Option 2", "A2");
|
||||
@@ -240,50 +178,40 @@ describe("PollCreateDialog", () => {
|
||||
dialog.find("button").simulate("click");
|
||||
const [, , eventType, sentEventContent] = mockClient.sendEvent.mock.calls[0];
|
||||
expect(M_POLL_START.matches(eventType)).toBeTruthy();
|
||||
expect(sentEventContent).toEqual(
|
||||
{
|
||||
[M_TEXT.name]: "Q\n1. A1\n2. A2",
|
||||
[M_POLL_START.name]: {
|
||||
"answers": [
|
||||
{
|
||||
"id": expect.any(String),
|
||||
[M_TEXT.name]: "A1",
|
||||
},
|
||||
{
|
||||
"id": expect.any(String),
|
||||
[M_TEXT.name]: "A2",
|
||||
},
|
||||
],
|
||||
"kind": M_POLL_KIND_DISCLOSED.name,
|
||||
"max_selections": 1,
|
||||
"question": {
|
||||
"body": "Q",
|
||||
"format": undefined,
|
||||
"formatted_body": undefined,
|
||||
"msgtype": "m.text",
|
||||
[M_TEXT.name]: "Q",
|
||||
expect(sentEventContent).toEqual({
|
||||
[M_TEXT.name]: "Q\n1. A1\n2. A2",
|
||||
[M_POLL_START.name]: {
|
||||
answers: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
[M_TEXT.name]: "A1",
|
||||
},
|
||||
{
|
||||
id: expect.any(String),
|
||||
[M_TEXT.name]: "A2",
|
||||
},
|
||||
],
|
||||
kind: M_POLL_KIND_DISCLOSED.name,
|
||||
max_selections: 1,
|
||||
question: {
|
||||
body: "Q",
|
||||
format: undefined,
|
||||
formatted_body: undefined,
|
||||
msgtype: "m.text",
|
||||
[M_TEXT.name]: "Q",
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("sends a poll edit event when editing", () => {
|
||||
const previousEvent: MatrixEvent = new MatrixEvent(
|
||||
PollStartEvent.from(
|
||||
"Poll Q",
|
||||
["Answer 1", "Answer 2"],
|
||||
M_POLL_KIND_DISCLOSED,
|
||||
).serialize(),
|
||||
PollStartEvent.from("Poll Q", ["Answer 1", "Answer 2"], M_POLL_KIND_DISCLOSED).serialize(),
|
||||
);
|
||||
previousEvent.event.event_id = "$prevEventId";
|
||||
|
||||
const dialog = mount(
|
||||
<PollCreateDialog
|
||||
room={createRoom()}
|
||||
onFinished={jest.fn()}
|
||||
editingMxEvent={previousEvent}
|
||||
/>,
|
||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} editingMxEvent={previousEvent} />,
|
||||
);
|
||||
|
||||
changeValue(dialog, "Question or topic", "Poll Q updated");
|
||||
@@ -293,65 +221,51 @@ describe("PollCreateDialog", () => {
|
||||
|
||||
const [, , eventType, sentEventContent] = mockClient.sendEvent.mock.calls[0];
|
||||
expect(M_POLL_START.matches(eventType)).toBeTruthy();
|
||||
expect(sentEventContent).toEqual(
|
||||
{
|
||||
"m.new_content": {
|
||||
[M_TEXT.name]: "Poll Q updated\n1. Answer 1\n2. Answer 2 updated",
|
||||
[M_POLL_START.name]: {
|
||||
"answers": [
|
||||
{
|
||||
"id": expect.any(String),
|
||||
[M_TEXT.name]: "Answer 1",
|
||||
},
|
||||
{
|
||||
"id": expect.any(String),
|
||||
[M_TEXT.name]: "Answer 2 updated",
|
||||
},
|
||||
],
|
||||
"kind": M_POLL_KIND_UNDISCLOSED.name,
|
||||
"max_selections": 1,
|
||||
"question": {
|
||||
"body": "Poll Q updated",
|
||||
"format": undefined,
|
||||
"formatted_body": undefined,
|
||||
"msgtype": "m.text",
|
||||
[M_TEXT.name]: "Poll Q updated",
|
||||
expect(sentEventContent).toEqual({
|
||||
"m.new_content": {
|
||||
[M_TEXT.name]: "Poll Q updated\n1. Answer 1\n2. Answer 2 updated",
|
||||
[M_POLL_START.name]: {
|
||||
answers: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
[M_TEXT.name]: "Answer 1",
|
||||
},
|
||||
{
|
||||
id: expect.any(String),
|
||||
[M_TEXT.name]: "Answer 2 updated",
|
||||
},
|
||||
],
|
||||
kind: M_POLL_KIND_UNDISCLOSED.name,
|
||||
max_selections: 1,
|
||||
question: {
|
||||
body: "Poll Q updated",
|
||||
format: undefined,
|
||||
formatted_body: undefined,
|
||||
msgtype: "m.text",
|
||||
[M_TEXT.name]: "Poll Q updated",
|
||||
},
|
||||
},
|
||||
"m.relates_to": {
|
||||
"event_id": previousEvent.getId(),
|
||||
"rel_type": "m.replace",
|
||||
},
|
||||
},
|
||||
);
|
||||
"m.relates_to": {
|
||||
event_id: previousEvent.getId(),
|
||||
rel_type: "m.replace",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createRoom(): Room {
|
||||
return new Room(
|
||||
"roomid",
|
||||
MatrixClientPeg.get(),
|
||||
"@name:example.com",
|
||||
{},
|
||||
);
|
||||
return new Room("roomid", MatrixClientPeg.get(), "@name:example.com", {});
|
||||
}
|
||||
|
||||
function changeValue(wrapper: ReactWrapper, labelText: string, value: string) {
|
||||
wrapper.find(`input[label="${labelText}"]`).simulate(
|
||||
"change",
|
||||
{ target: { value: value } },
|
||||
);
|
||||
wrapper.find(`input[label="${labelText}"]`).simulate("change", { target: { value: value } });
|
||||
}
|
||||
|
||||
function changeKind(wrapper: ReactWrapper, value: string) {
|
||||
wrapper.find("select").simulate(
|
||||
"change",
|
||||
{ target: { value: value } },
|
||||
);
|
||||
wrapper.find("select").simulate("change", { target: { value: value } });
|
||||
}
|
||||
|
||||
function submitIsDisabled(wrapper: ReactWrapper) {
|
||||
return wrapper.find('button[type="submit"]').prop("aria-disabled") === true;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
|
||||
import PowerSelector from "../../../../src/components/views/elements/PowerSelector";
|
||||
|
||||
describe('<PowerSelector />', () => {
|
||||
describe("<PowerSelector />", () => {
|
||||
it("should reset back to custom value when custom input is blurred blank", async () => {
|
||||
const fn = jest.fn();
|
||||
render(<PowerSelector value={25} maxValue={100} usersDefault={0} onChange={fn} />);
|
||||
|
||||
@@ -28,7 +28,9 @@ describe("<ProgressBar/>", () => {
|
||||
expect(progress.value).toBe(0);
|
||||
|
||||
// Await the animation to conclude to our initial value of 50
|
||||
act(() => { jest.runAllTimers(); });
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(progress.position).toBe(0.5);
|
||||
|
||||
// Move the needle to 80%
|
||||
@@ -36,7 +38,9 @@ describe("<ProgressBar/>", () => {
|
||||
expect(progress.position).toBe(0.5);
|
||||
|
||||
// Let the animaiton run a tiny bit, assert it has moved from where it was to where it needs to go
|
||||
act(() => { jest.advanceTimersByTime(150); });
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(150);
|
||||
});
|
||||
expect(progress.position).toBeGreaterThan(0.5);
|
||||
expect(progress.position).toBeLessThan(0.8);
|
||||
});
|
||||
|
||||
@@ -24,13 +24,13 @@ describe("<QRCode />", () => {
|
||||
|
||||
it("renders a QR with defaults", async () => {
|
||||
const { container, getAllByAltText } = render(<QRCode data="asd" />);
|
||||
await waitFor(() => getAllByAltText('QR Code').length === 1);
|
||||
await waitFor(() => getAllByAltText("QR Code").length === 1);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders a QR with high error correction level", async () => {
|
||||
const { container, getAllByAltText } = render(<QRCode data="asd" errorCorrectionLevel="high" />);
|
||||
await waitFor(() => getAllByAltText('QR Code').length === 1);
|
||||
await waitFor(() => getAllByAltText("QR Code").length === 1);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as testUtils from '../../../test-utils';
|
||||
import * as testUtils from "../../../test-utils";
|
||||
import { getParentEventId } from "../../../../src/utils/Reply";
|
||||
|
||||
describe("ReplyChain", () => {
|
||||
describe('getParentEventId', () => {
|
||||
it('retrieves relation reply from unedited event', () => {
|
||||
describe("getParentEventId", () => {
|
||||
it("retrieves relation reply from unedited event", () => {
|
||||
const originalEventWithRelation = testUtils.mkEvent({
|
||||
event: true,
|
||||
type: "m.room.message",
|
||||
@@ -28,7 +28,7 @@ describe("ReplyChain", () => {
|
||||
"body": "> Reply to this message\n\n foo",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og",
|
||||
event_id: "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -36,11 +36,12 @@ describe("ReplyChain", () => {
|
||||
room: "room_id",
|
||||
});
|
||||
|
||||
expect(getParentEventId(originalEventWithRelation))
|
||||
.toStrictEqual('$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og');
|
||||
expect(getParentEventId(originalEventWithRelation)).toStrictEqual(
|
||||
"$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og",
|
||||
);
|
||||
});
|
||||
|
||||
it('retrieves relation reply from original event when edited', () => {
|
||||
it("retrieves relation reply from original event when edited", () => {
|
||||
const originalEventWithRelation = testUtils.mkEvent({
|
||||
event: true,
|
||||
type: "m.room.message",
|
||||
@@ -49,7 +50,7 @@ describe("ReplyChain", () => {
|
||||
"body": "> Reply to this message\n\n foo",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og",
|
||||
event_id: "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -64,12 +65,12 @@ describe("ReplyChain", () => {
|
||||
"msgtype": "m.text",
|
||||
"body": "> Reply to this message\n\n * foo bar",
|
||||
"m.new_content": {
|
||||
"msgtype": "m.text",
|
||||
"body": "foo bar",
|
||||
msgtype: "m.text",
|
||||
body: "foo bar",
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": originalEventWithRelation.getId(),
|
||||
rel_type: "m.replace",
|
||||
event_id: originalEventWithRelation.getId(),
|
||||
},
|
||||
},
|
||||
user: "some_other_user",
|
||||
@@ -80,11 +81,12 @@ describe("ReplyChain", () => {
|
||||
originalEventWithRelation.makeReplaced(editEvent);
|
||||
|
||||
// The relation should be pulled from the original event
|
||||
expect(getParentEventId(originalEventWithRelation))
|
||||
.toStrictEqual('$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og');
|
||||
expect(getParentEventId(originalEventWithRelation)).toStrictEqual(
|
||||
"$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og",
|
||||
);
|
||||
});
|
||||
|
||||
it('retrieves relation reply from edit event when provided', () => {
|
||||
it("retrieves relation reply from edit event when provided", () => {
|
||||
const originalEvent = testUtils.mkEvent({
|
||||
event: true,
|
||||
type: "m.room.message",
|
||||
@@ -107,13 +109,13 @@ describe("ReplyChain", () => {
|
||||
"body": "foo bar",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og",
|
||||
event_id: "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og",
|
||||
},
|
||||
},
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": originalEvent.getId(),
|
||||
rel_type: "m.replace",
|
||||
event_id: originalEvent.getId(),
|
||||
},
|
||||
},
|
||||
user: "some_other_user",
|
||||
@@ -124,11 +126,10 @@ describe("ReplyChain", () => {
|
||||
originalEvent.makeReplaced(editEvent);
|
||||
|
||||
// The relation should be pulled from the edit event
|
||||
expect(getParentEventId(originalEvent))
|
||||
.toStrictEqual('$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og');
|
||||
expect(getParentEventId(originalEvent)).toStrictEqual("$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og");
|
||||
});
|
||||
|
||||
it('prefers relation reply from edit event over original event', () => {
|
||||
it("prefers relation reply from edit event over original event", () => {
|
||||
const originalEventWithRelation = testUtils.mkEvent({
|
||||
event: true,
|
||||
type: "m.room.message",
|
||||
@@ -137,7 +138,7 @@ describe("ReplyChain", () => {
|
||||
"body": "> Reply to this message\n\n foo",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$111",
|
||||
event_id: "$111",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -156,13 +157,13 @@ describe("ReplyChain", () => {
|
||||
"body": "foo bar",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$999",
|
||||
event_id: "$999",
|
||||
},
|
||||
},
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": originalEventWithRelation.getId(),
|
||||
rel_type: "m.replace",
|
||||
event_id: originalEventWithRelation.getId(),
|
||||
},
|
||||
},
|
||||
user: "some_other_user",
|
||||
@@ -173,10 +174,10 @@ describe("ReplyChain", () => {
|
||||
originalEventWithRelation.makeReplaced(editEvent);
|
||||
|
||||
// The relation should be pulled from the edit event
|
||||
expect(getParentEventId(originalEventWithRelation)).toStrictEqual('$999');
|
||||
expect(getParentEventId(originalEventWithRelation)).toStrictEqual("$999");
|
||||
});
|
||||
|
||||
it('able to clear relation reply from original event by providing empty relation field', () => {
|
||||
it("able to clear relation reply from original event by providing empty relation field", () => {
|
||||
const originalEventWithRelation = testUtils.mkEvent({
|
||||
event: true,
|
||||
type: "m.room.message",
|
||||
@@ -185,7 +186,7 @@ describe("ReplyChain", () => {
|
||||
"body": "> Reply to this message\n\n foo",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$111",
|
||||
event_id: "$111",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -206,8 +207,8 @@ describe("ReplyChain", () => {
|
||||
"m.relates_to": {},
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": originalEventWithRelation.getId(),
|
||||
rel_type: "m.replace",
|
||||
event_id: originalEventWithRelation.getId(),
|
||||
},
|
||||
},
|
||||
user: "some_other_user",
|
||||
|
||||
@@ -14,47 +14,47 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
|
||||
import StyledRadioGroup from "../../../../src/components/views/elements/StyledRadioGroup";
|
||||
|
||||
describe('<StyledRadioGroup />', () => {
|
||||
describe("<StyledRadioGroup />", () => {
|
||||
const optionA = {
|
||||
value: 'Anteater',
|
||||
value: "Anteater",
|
||||
label: <span>Anteater label</span>,
|
||||
description: 'anteater description',
|
||||
className: 'a-class',
|
||||
description: "anteater description",
|
||||
className: "a-class",
|
||||
};
|
||||
const optionB = {
|
||||
value: 'Badger',
|
||||
value: "Badger",
|
||||
label: <span>Badger label</span>,
|
||||
};
|
||||
const optionC = {
|
||||
value: 'Canary',
|
||||
value: "Canary",
|
||||
label: <span>Canary label</span>,
|
||||
description: <span>Canary description</span>,
|
||||
};
|
||||
const defaultDefinitions = [optionA, optionB, optionC];
|
||||
const defaultProps = {
|
||||
name: 'test',
|
||||
className: 'test-class',
|
||||
name: "test",
|
||||
className: "test-class",
|
||||
definitions: defaultDefinitions,
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
const getComponent = (props = {}) => render(<StyledRadioGroup {...defaultProps} {...props} />);
|
||||
|
||||
const getInputByValue = (component, value) => component.container.querySelector(`input[value="${value}"]`);
|
||||
const getCheckedInput = component => component.container.querySelector('input[checked]');
|
||||
const getCheckedInput = (component) => component.container.querySelector("input[checked]");
|
||||
|
||||
it('renders radios correctly when no value is provided', () => {
|
||||
it("renders radios correctly when no value is provided", () => {
|
||||
const component = getComponent();
|
||||
|
||||
expect(component.asFragment()).toMatchSnapshot();
|
||||
expect(getCheckedInput(component)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('selects correct button when value is provided', () => {
|
||||
it("selects correct button when value is provided", () => {
|
||||
const component = getComponent({
|
||||
value: optionC.value,
|
||||
});
|
||||
@@ -62,14 +62,11 @@ describe('<StyledRadioGroup />', () => {
|
||||
expect(getCheckedInput(component).value).toEqual(optionC.value);
|
||||
});
|
||||
|
||||
it('selects correct buttons when definitions have checked prop', () => {
|
||||
const definitions = [
|
||||
{ ...optionA, checked: true },
|
||||
optionB,
|
||||
{ ...optionC, checked: false },
|
||||
];
|
||||
it("selects correct buttons when definitions have checked prop", () => {
|
||||
const definitions = [{ ...optionA, checked: true }, optionB, { ...optionC, checked: false }];
|
||||
const component = getComponent({
|
||||
value: optionC.value, definitions,
|
||||
value: optionC.value,
|
||||
definitions,
|
||||
});
|
||||
|
||||
expect(getInputByValue(component, optionA.value)).toBeChecked();
|
||||
@@ -78,26 +75,22 @@ describe('<StyledRadioGroup />', () => {
|
||||
expect(getInputByValue(component, optionC.value)).not.toBeChecked();
|
||||
});
|
||||
|
||||
it('disables individual buttons based on definition.disabled', () => {
|
||||
const definitions = [
|
||||
optionA,
|
||||
{ ...optionB, disabled: true },
|
||||
{ ...optionC, disabled: true },
|
||||
];
|
||||
it("disables individual buttons based on definition.disabled", () => {
|
||||
const definitions = [optionA, { ...optionB, disabled: true }, { ...optionC, disabled: true }];
|
||||
const component = getComponent({ definitions });
|
||||
expect(getInputByValue(component, optionA.value)).not.toBeDisabled();
|
||||
expect(getInputByValue(component, optionB.value)).toBeDisabled();
|
||||
expect(getInputByValue(component, optionC.value)).toBeDisabled();
|
||||
});
|
||||
|
||||
it('disables all buttons with disabled prop', () => {
|
||||
it("disables all buttons with disabled prop", () => {
|
||||
const component = getComponent({ disabled: true });
|
||||
expect(getInputByValue(component, optionA.value)).toBeDisabled();
|
||||
expect(getInputByValue(component, optionB.value)).toBeDisabled();
|
||||
expect(getInputByValue(component, optionC.value)).toBeDisabled();
|
||||
});
|
||||
|
||||
it('calls onChange on click', () => {
|
||||
it("calls onChange on click", () => {
|
||||
const onChange = jest.fn();
|
||||
const component = getComponent({
|
||||
value: optionC.value,
|
||||
|
||||
@@ -15,29 +15,26 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
renderIntoDocument,
|
||||
Simulate,
|
||||
} from 'react-dom/test-utils';
|
||||
import { renderIntoDocument, Simulate } from "react-dom/test-utils";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import { Alignment } from '../../../../src/components/views/elements/Tooltip';
|
||||
import { Alignment } from "../../../../src/components/views/elements/Tooltip";
|
||||
import TooltipTarget from "../../../../src/components/views/elements/TooltipTarget";
|
||||
|
||||
describe('<TooltipTarget />', () => {
|
||||
describe("<TooltipTarget />", () => {
|
||||
const defaultProps = {
|
||||
"tooltipTargetClassName": 'test tooltipTargetClassName',
|
||||
"className": 'test className',
|
||||
"tooltipClassName": 'test tooltipClassName',
|
||||
"label": 'test label',
|
||||
"tooltipTargetClassName": "test tooltipTargetClassName",
|
||||
"className": "test className",
|
||||
"tooltipClassName": "test tooltipClassName",
|
||||
"label": "test label",
|
||||
"alignment": Alignment.Left,
|
||||
"id": 'test id',
|
||||
'data-test-id': 'test',
|
||||
"id": "test id",
|
||||
"data-test-id": "test",
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
// clean up renderer tooltips
|
||||
const wrapper = document.querySelector('.mx_Tooltip_wrapper');
|
||||
const wrapper = document.querySelector(".mx_Tooltip_wrapper");
|
||||
while (wrapper?.firstChild) {
|
||||
wrapper.removeChild(wrapper.lastChild);
|
||||
}
|
||||
@@ -45,19 +42,19 @@ describe('<TooltipTarget />', () => {
|
||||
|
||||
const getComponent = (props = {}) => {
|
||||
const wrapper = renderIntoDocument<HTMLSpanElement>(
|
||||
// wrap in element so renderIntoDocument can render functional component
|
||||
// wrap in element so renderIntoDocument can render functional component
|
||||
<span>
|
||||
<TooltipTarget {...defaultProps} {...props}>
|
||||
<span>child</span>
|
||||
</TooltipTarget>
|
||||
</span>,
|
||||
) as HTMLSpanElement;
|
||||
return wrapper.querySelector('[data-test-id=test]');
|
||||
return wrapper.querySelector("[data-test-id=test]");
|
||||
};
|
||||
|
||||
const getVisibleTooltip = () => document.querySelector('.mx_Tooltip.mx_Tooltip_visible');
|
||||
const getVisibleTooltip = () => document.querySelector(".mx_Tooltip.mx_Tooltip_visible");
|
||||
|
||||
it('renders container', () => {
|
||||
it("renders container", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(getVisibleTooltip()).toBeFalsy();
|
||||
@@ -72,7 +69,7 @@ describe('<TooltipTarget />', () => {
|
||||
expect(getVisibleTooltip()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('hides tooltip on mouseleave', () => {
|
||||
it("hides tooltip on mouseleave", () => {
|
||||
const wrapper = getComponent();
|
||||
act(() => {
|
||||
Simulate.mouseOver(wrapper);
|
||||
@@ -84,7 +81,7 @@ describe('<TooltipTarget />', () => {
|
||||
expect(getVisibleTooltip()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('displays tooltip on focus', () => {
|
||||
it("displays tooltip on focus", () => {
|
||||
const wrapper = getComponent();
|
||||
act(() => {
|
||||
Simulate.focus(wrapper);
|
||||
@@ -92,7 +89,7 @@ describe('<TooltipTarget />', () => {
|
||||
expect(getVisibleTooltip()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('hides tooltip on blur', async () => {
|
||||
it("hides tooltip on blur", async () => {
|
||||
const wrapper = getComponent();
|
||||
act(() => {
|
||||
Simulate.focus(wrapper);
|
||||
|
||||
@@ -14,51 +14,52 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mount } from "enzyme";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import LiveDurationDropdown, { DEFAULT_DURATION_MS }
|
||||
from '../../../../src/components/views/location/LiveDurationDropdown';
|
||||
import { findById, mockPlatformPeg } from '../../../test-utils';
|
||||
import LiveDurationDropdown, {
|
||||
DEFAULT_DURATION_MS,
|
||||
} from "../../../../src/components/views/location/LiveDurationDropdown";
|
||||
import { findById, mockPlatformPeg } from "../../../test-utils";
|
||||
|
||||
mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) });
|
||||
|
||||
describe('<LiveDurationDropdown />', () => {
|
||||
describe("<LiveDurationDropdown />", () => {
|
||||
const defaultProps = {
|
||||
timeout: DEFAULT_DURATION_MS,
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<LiveDurationDropdown {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => mount(<LiveDurationDropdown {...defaultProps} {...props} />);
|
||||
|
||||
const getOption = (wrapper, timeout) => findById(wrapper, `live-duration__${timeout}`).at(0);
|
||||
const getSelectedOption = (wrapper) => findById(wrapper, 'live-duration_value');
|
||||
const openDropdown = (wrapper) => act(() => {
|
||||
wrapper.find('[role="button"]').at(0).simulate('click');
|
||||
wrapper.setProps({});
|
||||
});
|
||||
const getSelectedOption = (wrapper) => findById(wrapper, "live-duration_value");
|
||||
const openDropdown = (wrapper) =>
|
||||
act(() => {
|
||||
wrapper.find('[role="button"]').at(0).simulate("click");
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
it('renders timeout as selected option', () => {
|
||||
it("renders timeout as selected option", () => {
|
||||
const wrapper = getComponent();
|
||||
expect(getSelectedOption(wrapper).text()).toEqual('Share for 15m');
|
||||
expect(getSelectedOption(wrapper).text()).toEqual("Share for 15m");
|
||||
});
|
||||
|
||||
it('renders non-default timeout as selected option', () => {
|
||||
it("renders non-default timeout as selected option", () => {
|
||||
const timeout = 1234567;
|
||||
const wrapper = getComponent({ timeout });
|
||||
expect(getSelectedOption(wrapper).text()).toEqual(`Share for 21m`);
|
||||
});
|
||||
|
||||
it('renders a dropdown option for a non-default timeout value', () => {
|
||||
it("renders a dropdown option for a non-default timeout value", () => {
|
||||
const timeout = 1234567;
|
||||
const wrapper = getComponent({ timeout });
|
||||
openDropdown(wrapper);
|
||||
expect(getOption(wrapper, timeout).text()).toEqual(`Share for 21m`);
|
||||
});
|
||||
|
||||
it('updates value on option selection', () => {
|
||||
it("updates value on option selection", () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = getComponent({ onChange });
|
||||
|
||||
@@ -67,7 +68,7 @@ describe('<LiveDurationDropdown />', () => {
|
||||
openDropdown(wrapper);
|
||||
|
||||
act(() => {
|
||||
getOption(wrapper, ONE_HOUR).simulate('click');
|
||||
getOption(wrapper, ONE_HOUR).simulate("click");
|
||||
});
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(ONE_HOUR);
|
||||
|
||||
@@ -14,34 +14,34 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import maplibregl from "maplibre-gl";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from "enzyme";
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { logger } from 'matrix-js-sdk/src/logger';
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { mocked } from "jest-mock";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import LocationPicker from "../../../../src/components/views/location/LocationPicker";
|
||||
import { LocationShareType } from "../../../../src/components/views/location/shareLocation";
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||
import { findById, findByTestId, mockPlatformPeg } from '../../../test-utils';
|
||||
import { findMapStyleUrl, LocationShareError } from '../../../../src/utils/location';
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { findById, findByTestId, mockPlatformPeg } from "../../../test-utils";
|
||||
import { findMapStyleUrl, LocationShareError } from "../../../../src/utils/location";
|
||||
|
||||
jest.mock('../../../../src/utils/location/findMapStyleUrl', () => ({
|
||||
findMapStyleUrl: jest.fn().mockReturnValue('tileserver.com'),
|
||||
jest.mock("../../../../src/utils/location/findMapStyleUrl", () => ({
|
||||
findMapStyleUrl: jest.fn().mockReturnValue("tileserver.com"),
|
||||
}));
|
||||
|
||||
// dropdown uses this
|
||||
mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) });
|
||||
|
||||
describe("LocationPicker", () => {
|
||||
describe('<LocationPicker />', () => {
|
||||
const roomId = '!room:server.org';
|
||||
const userId = '@user:server.org';
|
||||
describe("<LocationPicker />", () => {
|
||||
const roomId = "!room:server.org";
|
||||
const userId = "@user:server.org";
|
||||
const sender = new RoomMember(roomId, userId);
|
||||
const defaultProps = {
|
||||
sender,
|
||||
@@ -56,10 +56,11 @@ describe("LocationPicker", () => {
|
||||
isGuest: jest.fn(),
|
||||
getClientWellKnown: jest.fn(),
|
||||
};
|
||||
const getComponent = (props = {}) => mount(<LocationPicker {...defaultProps} {...props} />, {
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: { value: mockClient },
|
||||
});
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<LocationPicker {...defaultProps} {...props} />, {
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: { value: mockClient },
|
||||
});
|
||||
|
||||
const mockMap = new maplibregl.Map();
|
||||
const mockGeolocate = new maplibregl.GeolocateControl();
|
||||
@@ -82,33 +83,33 @@ describe("LocationPicker", () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(logger, 'error').mockRestore();
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient as unknown as MatrixClient);
|
||||
jest.spyOn(logger, "error").mockRestore();
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient as unknown as MatrixClient);
|
||||
jest.clearAllMocks();
|
||||
mocked(mockMap).addControl.mockReset();
|
||||
mocked(findMapStyleUrl).mockReturnValue('tileserver.com');
|
||||
mocked(findMapStyleUrl).mockReturnValue("tileserver.com");
|
||||
});
|
||||
|
||||
it('displays error when map emits an error', () => {
|
||||
it("displays error when map emits an error", () => {
|
||||
// suppress expected error log
|
||||
jest.spyOn(logger, 'error').mockImplementation(() => { });
|
||||
jest.spyOn(logger, "error").mockImplementation(() => {});
|
||||
const wrapper = getComponent();
|
||||
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mocked(mockMap).emit('error', { error: 'Something went wrong' });
|
||||
mocked(mockMap).emit("error", { error: "Something went wrong" });
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual(
|
||||
"This homeserver is not configured correctly to display maps, "
|
||||
+ "or the configured map server may be unreachable.",
|
||||
expect(findByTestId(wrapper, "map-rendering-error").find("p").text()).toEqual(
|
||||
"This homeserver is not configured correctly to display maps, " +
|
||||
"or the configured map server may be unreachable.",
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error when map display is not configured properly', () => {
|
||||
it("displays error when map display is not configured properly", () => {
|
||||
// suppress expected error log
|
||||
jest.spyOn(logger, 'error').mockImplementation(() => { });
|
||||
jest.spyOn(logger, "error").mockImplementation(() => {});
|
||||
mocked(findMapStyleUrl).mockImplementation(() => {
|
||||
throw new Error(LocationShareError.MapStyleUrlNotConfigured);
|
||||
});
|
||||
@@ -116,111 +117,111 @@ describe("LocationPicker", () => {
|
||||
const wrapper = getComponent();
|
||||
wrapper.setProps({});
|
||||
|
||||
expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual(
|
||||
expect(findByTestId(wrapper, "map-rendering-error").find("p").text()).toEqual(
|
||||
"This homeserver is not configured to display maps.",
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error when map setup throws', () => {
|
||||
it("displays error when map setup throws", () => {
|
||||
// suppress expected error log
|
||||
jest.spyOn(logger, 'error').mockImplementation(() => { });
|
||||
jest.spyOn(logger, "error").mockImplementation(() => {});
|
||||
|
||||
// throw an error
|
||||
mocked(mockMap).addControl.mockImplementation(() => { throw new Error('oups'); });
|
||||
mocked(mockMap).addControl.mockImplementation(() => {
|
||||
throw new Error("oups");
|
||||
});
|
||||
|
||||
const wrapper = getComponent();
|
||||
wrapper.setProps({});
|
||||
|
||||
expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual(
|
||||
"This homeserver is not configured correctly to display maps, "
|
||||
+ "or the configured map server may be unreachable.",
|
||||
expect(findByTestId(wrapper, "map-rendering-error").find("p").text()).toEqual(
|
||||
"This homeserver is not configured correctly to display maps, " +
|
||||
"or the configured map server may be unreachable.",
|
||||
);
|
||||
});
|
||||
|
||||
it('initiates map with geolocation', () => {
|
||||
it("initiates map with geolocation", () => {
|
||||
getComponent();
|
||||
|
||||
expect(mockMap.addControl).toHaveBeenCalledWith(mockGeolocate);
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mocked(mockMap).emit('load');
|
||||
mocked(mockMap).emit("load");
|
||||
});
|
||||
|
||||
expect(mockGeolocate.trigger).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const testUserLocationShareTypes = (shareType: LocationShareType.Own | LocationShareType.Live) => {
|
||||
describe('user location behaviours', () => {
|
||||
it('closes and displays error when geolocation errors', () => {
|
||||
describe("user location behaviours", () => {
|
||||
it("closes and displays error when geolocation errors", () => {
|
||||
// suppress expected error log
|
||||
jest.spyOn(logger, 'error').mockImplementation(() => { });
|
||||
jest.spyOn(logger, "error").mockImplementation(() => {});
|
||||
const onFinished = jest.fn();
|
||||
getComponent({ onFinished, shareType });
|
||||
|
||||
expect(mockMap.addControl).toHaveBeenCalledWith(mockGeolocate);
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mockMap.emit('load');
|
||||
mockMap.emit("load");
|
||||
// @ts-ignore
|
||||
mockGeolocate.emit('error', {});
|
||||
mockGeolocate.emit("error", {});
|
||||
});
|
||||
|
||||
// dialog is closed on error
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('sets position on geolocate event', () => {
|
||||
it("sets position on geolocate event", () => {
|
||||
const wrapper = getComponent({ shareType });
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mocked(mockGeolocate).emit('geolocate', mockGeolocationPosition);
|
||||
mocked(mockGeolocate).emit("geolocate", mockGeolocationPosition);
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
// marker added
|
||||
expect(maplibregl.Marker).toHaveBeenCalled();
|
||||
expect(mockMarker.setLngLat).toHaveBeenCalledWith(new maplibregl.LngLat(
|
||||
12.4, 43.2,
|
||||
));
|
||||
expect(mockMarker.setLngLat).toHaveBeenCalledWith(new maplibregl.LngLat(12.4, 43.2));
|
||||
// submit button is enabled when position is truthy
|
||||
expect(findByTestId(wrapper, 'location-picker-submit-button').at(0).props().disabled).toBeFalsy();
|
||||
expect(wrapper.find('MemberAvatar').length).toBeTruthy();
|
||||
expect(findByTestId(wrapper, "location-picker-submit-button").at(0).props().disabled).toBeFalsy();
|
||||
expect(wrapper.find("MemberAvatar").length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('disables submit button until geolocation completes', () => {
|
||||
it("disables submit button until geolocation completes", () => {
|
||||
const onChoose = jest.fn();
|
||||
const wrapper = getComponent({ shareType, onChoose });
|
||||
|
||||
// submit button is enabled when position is truthy
|
||||
expect(findByTestId(wrapper, 'location-picker-submit-button').at(0).props().disabled).toBeTruthy();
|
||||
expect(findByTestId(wrapper, "location-picker-submit-button").at(0).props().disabled).toBeTruthy();
|
||||
act(() => {
|
||||
findByTestId(wrapper, 'location-picker-submit-button').at(0).simulate('click');
|
||||
findByTestId(wrapper, "location-picker-submit-button").at(0).simulate("click");
|
||||
});
|
||||
// nothing happens on button click
|
||||
expect(onChoose).not.toHaveBeenCalled();
|
||||
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mocked(mockGeolocate).emit('geolocate', mockGeolocationPosition);
|
||||
mocked(mockGeolocate).emit("geolocate", mockGeolocationPosition);
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
// submit button is enabled when position is truthy
|
||||
expect(findByTestId(wrapper, 'location-picker-submit-button').at(0).props().disabled).toBeFalsy();
|
||||
expect(findByTestId(wrapper, "location-picker-submit-button").at(0).props().disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('submits location', () => {
|
||||
it("submits location", () => {
|
||||
const onChoose = jest.fn();
|
||||
const wrapper = getComponent({ onChoose, shareType });
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mocked(mockGeolocate).emit('geolocate', mockGeolocationPosition);
|
||||
mocked(mockGeolocate).emit("geolocate", mockGeolocationPosition);
|
||||
// make sure button is enabled
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
act(() => {
|
||||
findByTestId(wrapper, 'location-picker-submit-button').at(0).simulate('click');
|
||||
findByTestId(wrapper, "location-picker-submit-button").at(0).simulate("click");
|
||||
});
|
||||
|
||||
// content of this call is tested in LocationShareMenu-test
|
||||
@@ -229,67 +230,68 @@ describe("LocationPicker", () => {
|
||||
});
|
||||
};
|
||||
|
||||
describe('for Own location share type', () => {
|
||||
describe("for Own location share type", () => {
|
||||
testUserLocationShareTypes(LocationShareType.Own);
|
||||
});
|
||||
|
||||
describe('for Live location share type', () => {
|
||||
describe("for Live location share type", () => {
|
||||
const shareType = LocationShareType.Live;
|
||||
testUserLocationShareTypes(shareType);
|
||||
|
||||
const getOption = (wrapper, timeout) => findById(wrapper, `live-duration__${timeout}`).at(0);
|
||||
const getDropdown = wrapper => findByTestId(wrapper, 'live-duration-dropdown');
|
||||
const getSelectedOption = (wrapper) => findById(wrapper, 'live-duration_value');
|
||||
const getDropdown = (wrapper) => findByTestId(wrapper, "live-duration-dropdown");
|
||||
const getSelectedOption = (wrapper) => findById(wrapper, "live-duration_value");
|
||||
|
||||
const openDropdown = (wrapper) => act(() => {
|
||||
const dropdown = getDropdown(wrapper);
|
||||
dropdown.find('[role="button"]').at(0).simulate('click');
|
||||
wrapper.setProps({});
|
||||
});
|
||||
const openDropdown = (wrapper) =>
|
||||
act(() => {
|
||||
const dropdown = getDropdown(wrapper);
|
||||
dropdown.find('[role="button"]').at(0).simulate("click");
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
it('renders live duration dropdown with default option', () => {
|
||||
it("renders live duration dropdown with default option", () => {
|
||||
const wrapper = getComponent({ shareType });
|
||||
expect(getSelectedOption(getDropdown(wrapper)).text()).toEqual('Share for 15m');
|
||||
expect(getSelectedOption(getDropdown(wrapper)).text()).toEqual("Share for 15m");
|
||||
});
|
||||
|
||||
it('updates selected duration', () => {
|
||||
it("updates selected duration", () => {
|
||||
const wrapper = getComponent({ shareType });
|
||||
|
||||
openDropdown(wrapper);
|
||||
const dropdown = getDropdown(wrapper);
|
||||
act(() => {
|
||||
getOption(dropdown, 3600000).simulate('click');
|
||||
getOption(dropdown, 3600000).simulate("click");
|
||||
});
|
||||
|
||||
// value updated
|
||||
expect(getSelectedOption(getDropdown(wrapper)).text()).toEqual('Share for 1h');
|
||||
expect(getSelectedOption(getDropdown(wrapper)).text()).toEqual("Share for 1h");
|
||||
});
|
||||
});
|
||||
|
||||
describe('for Pin drop location share type', () => {
|
||||
describe("for Pin drop location share type", () => {
|
||||
const shareType = LocationShareType.Pin;
|
||||
it('initiates map with geolocation', () => {
|
||||
it("initiates map with geolocation", () => {
|
||||
getComponent({ shareType });
|
||||
|
||||
expect(mockMap.addControl).toHaveBeenCalledWith(mockGeolocate);
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mocked(mockMap).emit('load');
|
||||
mocked(mockMap).emit("load");
|
||||
});
|
||||
|
||||
expect(mockGeolocate.trigger).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('removes geolocation control on geolocation error', () => {
|
||||
it("removes geolocation control on geolocation error", () => {
|
||||
// suppress expected error log
|
||||
jest.spyOn(logger, 'error').mockImplementation(() => { });
|
||||
jest.spyOn(logger, "error").mockImplementation(() => {});
|
||||
const onFinished = jest.fn();
|
||||
getComponent({ onFinished, shareType });
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mockMap.emit('load');
|
||||
mockMap.emit("load");
|
||||
// @ts-ignore
|
||||
mockGeolocate.emit('error', {});
|
||||
mockGeolocate.emit("error", {});
|
||||
});
|
||||
|
||||
expect(mockMap.removeControl).toHaveBeenCalledWith(mockGeolocate);
|
||||
@@ -297,47 +299,45 @@ describe("LocationPicker", () => {
|
||||
expect(onFinished).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not set position on geolocate event', () => {
|
||||
it("does not set position on geolocate event", () => {
|
||||
mocked(maplibregl.Marker).mockClear();
|
||||
const wrapper = getComponent({ shareType });
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mocked(mockGeolocate).emit('geolocate', mockGeolocationPosition);
|
||||
mocked(mockGeolocate).emit("geolocate", mockGeolocationPosition);
|
||||
});
|
||||
|
||||
// marker not added
|
||||
expect(wrapper.find('Marker').length).toBeFalsy();
|
||||
expect(wrapper.find("Marker").length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('sets position on click event', () => {
|
||||
it("sets position on click event", () => {
|
||||
const wrapper = getComponent({ shareType });
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mocked(mockMap).emit('click', mockClickEvent);
|
||||
mocked(mockMap).emit("click", mockClickEvent);
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
// marker added
|
||||
expect(maplibregl.Marker).toHaveBeenCalled();
|
||||
expect(mockMarker.setLngLat).toHaveBeenCalledWith(new maplibregl.LngLat(
|
||||
12.4, 43.2,
|
||||
));
|
||||
expect(mockMarker.setLngLat).toHaveBeenCalledWith(new maplibregl.LngLat(12.4, 43.2));
|
||||
|
||||
// marker is set, icon not avatar
|
||||
expect(wrapper.find('.mx_Marker_icon').length).toBeTruthy();
|
||||
expect(wrapper.find(".mx_Marker_icon").length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('submits location', () => {
|
||||
it("submits location", () => {
|
||||
const onChoose = jest.fn();
|
||||
const wrapper = getComponent({ onChoose, shareType });
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
mocked(mockMap).emit('click', mockClickEvent);
|
||||
mocked(mockMap).emit("click", mockClickEvent);
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
act(() => {
|
||||
findByTestId(wrapper, 'location-picker-submit-button').at(0).simulate('click');
|
||||
findByTestId(wrapper, "location-picker-submit-button").at(0).simulate("click");
|
||||
});
|
||||
|
||||
// content of this call is tested in LocationShareMenu-test
|
||||
|
||||
@@ -14,43 +14,43 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount, ReactWrapper } from 'enzyme';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
|
||||
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||
import { RelationType } from 'matrix-js-sdk/src/matrix';
|
||||
import { logger } from 'matrix-js-sdk/src/logger';
|
||||
import { M_ASSET, LocationAssetType } from 'matrix-js-sdk/src/@types/location';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mount, ReactWrapper } from "enzyme";
|
||||
import { mocked } from "jest-mock";
|
||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { RelationType } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { M_ASSET, LocationAssetType } from "matrix-js-sdk/src/@types/location";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import LocationShareMenu from '../../../../src/components/views/location/LocationShareMenu';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import { ChevronFace } from '../../../../src/components/structures/ContextMenu';
|
||||
import SettingsStore from '../../../../src/settings/SettingsStore';
|
||||
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||
import { LocationShareType } from '../../../../src/components/views/location/shareLocation';
|
||||
import LocationShareMenu from "../../../../src/components/views/location/LocationShareMenu";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import { ChevronFace } from "../../../../src/components/structures/ContextMenu";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { LocationShareType } from "../../../../src/components/views/location/shareLocation";
|
||||
import {
|
||||
findByTagAndTestId,
|
||||
findByTestId,
|
||||
flushPromisesWithFakeTimers,
|
||||
getMockClientWithEventEmitter,
|
||||
setupAsyncStoreWithClient,
|
||||
} from '../../../test-utils';
|
||||
import Modal from '../../../../src/Modal';
|
||||
import { DEFAULT_DURATION_MS } from '../../../../src/components/views/location/LiveDurationDropdown';
|
||||
import { OwnBeaconStore } from '../../../../src/stores/OwnBeaconStore';
|
||||
import { SettingLevel } from '../../../../src/settings/SettingLevel';
|
||||
import QuestionDialog from '../../../../src/components/views/dialogs/QuestionDialog';
|
||||
} from "../../../test-utils";
|
||||
import Modal from "../../../../src/Modal";
|
||||
import { DEFAULT_DURATION_MS } from "../../../../src/components/views/location/LiveDurationDropdown";
|
||||
import { OwnBeaconStore } from "../../../../src/stores/OwnBeaconStore";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
import QuestionDialog from "../../../../src/components/views/dialogs/QuestionDialog";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
jest.mock('../../../../src/utils/location/findMapStyleUrl', () => ({
|
||||
findMapStyleUrl: jest.fn().mockReturnValue('test'),
|
||||
jest.mock("../../../../src/utils/location/findMapStyleUrl", () => ({
|
||||
findMapStyleUrl: jest.fn().mockReturnValue("test"),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../src/settings/SettingsStore', () => ({
|
||||
jest.mock("../../../../src/settings/SettingsStore", () => ({
|
||||
getValue: jest.fn(),
|
||||
setValue: jest.fn(),
|
||||
monitorSetting: jest.fn(),
|
||||
@@ -58,44 +58,45 @@ jest.mock('../../../../src/settings/SettingsStore', () => ({
|
||||
unwatchSetting: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../src/stores/OwnProfileStore', () => ({
|
||||
jest.mock("../../../../src/stores/OwnProfileStore", () => ({
|
||||
OwnProfileStore: {
|
||||
instance: {
|
||||
displayName: 'Ernie',
|
||||
getHttpAvatarUrl: jest.fn().mockReturnValue('image.com/img'),
|
||||
displayName: "Ernie",
|
||||
getHttpAvatarUrl: jest.fn().mockReturnValue("image.com/img"),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../../../../src/Modal', () => ({
|
||||
jest.mock("../../../../src/Modal", () => ({
|
||||
createDialog: jest.fn(),
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
ModalManagerEvent: { Opened: "opened" },
|
||||
}));
|
||||
|
||||
describe('<LocationShareMenu />', () => {
|
||||
const userId = '@ernie:server.org';
|
||||
describe("<LocationShareMenu />", () => {
|
||||
const userId = "@ernie:server.org";
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getUserId: jest.fn().mockReturnValue(userId),
|
||||
getClientWellKnown: jest.fn().mockResolvedValue({
|
||||
map_style_url: 'maps.com',
|
||||
map_style_url: "maps.com",
|
||||
}),
|
||||
sendMessage: jest.fn(),
|
||||
unstable_createLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }),
|
||||
unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }),
|
||||
unstable_createLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }),
|
||||
unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }),
|
||||
getVisibleRooms: jest.fn().mockReturnValue([]),
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
menuPosition: {
|
||||
top: 1, left: 1,
|
||||
top: 1,
|
||||
left: 1,
|
||||
chevronFace: ChevronFace.Bottom,
|
||||
},
|
||||
onFinished: jest.fn(),
|
||||
openMenu: jest.fn(),
|
||||
roomId: '!room:server.org',
|
||||
sender: new RoomMember('!room:server.org', userId),
|
||||
roomId: "!room:server.org",
|
||||
sender: new RoomMember("!room:server.org", userId),
|
||||
};
|
||||
|
||||
const position = {
|
||||
@@ -105,7 +106,7 @@ describe('<LocationShareMenu />', () => {
|
||||
accuracy: 10,
|
||||
},
|
||||
timestamp: 1646305006802,
|
||||
type: 'geolocate',
|
||||
type: "geolocate",
|
||||
};
|
||||
|
||||
const makeOwnBeaconStore = async () => {
|
||||
@@ -122,11 +123,11 @@ describe('<LocationShareMenu />', () => {
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.spyOn(logger, 'error').mockRestore();
|
||||
jest.spyOn(logger, "error").mockRestore();
|
||||
mocked(SettingsStore).getValue.mockReturnValue(false);
|
||||
mockClient.sendMessage.mockClear();
|
||||
mockClient.unstable_createLiveBeacon.mockClear().mockResolvedValue({ event_id: '1' });
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient as unknown as MatrixClient);
|
||||
mockClient.unstable_createLiveBeacon.mockClear().mockResolvedValue({ event_id: "1" });
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient as unknown as MatrixClient);
|
||||
mocked(Modal).createDialog.mockClear();
|
||||
|
||||
jest.clearAllMocks();
|
||||
@@ -135,20 +136,20 @@ describe('<LocationShareMenu />', () => {
|
||||
});
|
||||
|
||||
const getShareTypeOption = (component: ReactWrapper, shareType: LocationShareType) =>
|
||||
findByTagAndTestId(component, `share-location-option-${shareType}`, 'button');
|
||||
findByTagAndTestId(component, `share-location-option-${shareType}`, "button");
|
||||
|
||||
const getBackButton = (component: ReactWrapper) =>
|
||||
findByTagAndTestId(component, 'share-dialog-buttons-back', 'button');
|
||||
findByTagAndTestId(component, "share-dialog-buttons-back", "button");
|
||||
|
||||
const getCancelButton = (component: ReactWrapper) =>
|
||||
findByTagAndTestId(component, 'share-dialog-buttons-cancel', 'button');
|
||||
findByTagAndTestId(component, "share-dialog-buttons-cancel", "button");
|
||||
|
||||
const getSubmitButton = (component: ReactWrapper) =>
|
||||
findByTagAndTestId(component, 'location-picker-submit-button', 'button');
|
||||
findByTagAndTestId(component, "location-picker-submit-button", "button");
|
||||
|
||||
const setLocation = (component: ReactWrapper) => {
|
||||
// set the location
|
||||
const locationPickerInstance = component.find('LocationPicker').instance();
|
||||
const locationPickerInstance = component.find("LocationPicker").instance();
|
||||
act(() => {
|
||||
// @ts-ignore
|
||||
locationPickerInstance.onGeolocate(position);
|
||||
@@ -159,38 +160,38 @@ describe('<LocationShareMenu />', () => {
|
||||
|
||||
const setShareType = (component: ReactWrapper, shareType: LocationShareType) =>
|
||||
act(() => {
|
||||
getShareTypeOption(component, shareType).at(0).simulate('click');
|
||||
getShareTypeOption(component, shareType).at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
describe('when only Own share type is enabled', () => {
|
||||
describe("when only Own share type is enabled", () => {
|
||||
beforeEach(() => enableSettings([]));
|
||||
|
||||
it('renders own and live location options', () => {
|
||||
it("renders own and live location options", () => {
|
||||
const component = getComponent();
|
||||
expect(getShareTypeOption(component, LocationShareType.Own).length).toBe(1);
|
||||
expect(getShareTypeOption(component, LocationShareType.Live).length).toBe(1);
|
||||
});
|
||||
|
||||
it('renders back button from location picker screen', () => {
|
||||
it("renders back button from location picker screen", () => {
|
||||
const component = getComponent();
|
||||
setShareType(component, LocationShareType.Own);
|
||||
|
||||
expect(getBackButton(component).length).toBe(1);
|
||||
});
|
||||
|
||||
it('clicking cancel button from location picker closes dialog', () => {
|
||||
it("clicking cancel button from location picker closes dialog", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
|
||||
act(() => {
|
||||
getCancelButton(component).at(0).simulate('click');
|
||||
getCancelButton(component).at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates static own location share event on submission', () => {
|
||||
it("creates static own location share event on submission", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
|
||||
@@ -199,7 +200,7 @@ describe('<LocationShareMenu />', () => {
|
||||
setLocation(component);
|
||||
|
||||
act(() => {
|
||||
getSubmitButton(component).at(0).simulate('click');
|
||||
getSubmitButton(component).at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
@@ -207,66 +208,68 @@ describe('<LocationShareMenu />', () => {
|
||||
const [messageRoomId, relation, messageBody] = mockClient.sendMessage.mock.calls[0];
|
||||
expect(messageRoomId).toEqual(defaultProps.roomId);
|
||||
expect(relation).toEqual(null);
|
||||
expect(messageBody).toEqual(expect.objectContaining({
|
||||
[M_ASSET.name]: {
|
||||
type: LocationAssetType.Self,
|
||||
},
|
||||
}));
|
||||
expect(messageBody).toEqual(
|
||||
expect.objectContaining({
|
||||
[M_ASSET.name]: {
|
||||
type: LocationAssetType.Self,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with pin drop share type enabled', () => {
|
||||
it('renders share type switch with own and pin drop options', () => {
|
||||
describe("with pin drop share type enabled", () => {
|
||||
it("renders share type switch with own and pin drop options", () => {
|
||||
const component = getComponent();
|
||||
expect(component.find('LocationPicker').length).toBe(0);
|
||||
expect(component.find("LocationPicker").length).toBe(0);
|
||||
|
||||
expect(getShareTypeOption(component, LocationShareType.Own).length).toBe(1);
|
||||
expect(getShareTypeOption(component, LocationShareType.Pin).length).toBe(1);
|
||||
});
|
||||
|
||||
it('does not render back button on share type screen', () => {
|
||||
it("does not render back button on share type screen", () => {
|
||||
const component = getComponent();
|
||||
expect(getBackButton(component).length).toBe(0);
|
||||
});
|
||||
|
||||
it('clicking cancel button from share type screen closes dialog', () => {
|
||||
it("clicking cancel button from share type screen closes dialog", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
|
||||
act(() => {
|
||||
getCancelButton(component).at(0).simulate('click');
|
||||
getCancelButton(component).at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('selecting own location share type advances to location picker', () => {
|
||||
it("selecting own location share type advances to location picker", () => {
|
||||
const component = getComponent();
|
||||
|
||||
setShareType(component, LocationShareType.Own);
|
||||
|
||||
expect(component.find('LocationPicker').length).toBe(1);
|
||||
expect(component.find("LocationPicker").length).toBe(1);
|
||||
});
|
||||
|
||||
it('clicking back button from location picker screen goes back to share screen', () => {
|
||||
it("clicking back button from location picker screen goes back to share screen", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
|
||||
// advance to location picker
|
||||
setShareType(component, LocationShareType.Own);
|
||||
|
||||
expect(component.find('LocationPicker').length).toBe(1);
|
||||
expect(component.find("LocationPicker").length).toBe(1);
|
||||
|
||||
act(() => {
|
||||
getBackButton(component).at(0).simulate('click');
|
||||
getBackButton(component).at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
// back to share type
|
||||
expect(component.find('ShareType').length).toBe(1);
|
||||
expect(component.find("ShareType").length).toBe(1);
|
||||
});
|
||||
|
||||
it('creates pin drop location share event on submission', () => {
|
||||
it("creates pin drop location share event on submission", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
|
||||
@@ -276,7 +279,7 @@ describe('<LocationShareMenu />', () => {
|
||||
setLocation(component);
|
||||
|
||||
act(() => {
|
||||
getSubmitButton(component).at(0).simulate('click');
|
||||
getSubmitButton(component).at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
@@ -284,76 +287,81 @@ describe('<LocationShareMenu />', () => {
|
||||
const [messageRoomId, relation, messageBody] = mockClient.sendMessage.mock.calls[0];
|
||||
expect(messageRoomId).toEqual(defaultProps.roomId);
|
||||
expect(relation).toEqual(null);
|
||||
expect(messageBody).toEqual(expect.objectContaining({
|
||||
[M_ASSET.name]: {
|
||||
type: LocationAssetType.Pin,
|
||||
},
|
||||
}));
|
||||
expect(messageBody).toEqual(
|
||||
expect.objectContaining({
|
||||
[M_ASSET.name]: {
|
||||
type: LocationAssetType.Pin,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with live location disabled', () => {
|
||||
describe("with live location disabled", () => {
|
||||
beforeEach(() => enableSettings([]));
|
||||
|
||||
const getToggle = (component: ReactWrapper) =>
|
||||
findByTestId(component, 'enable-live-share-toggle').find('[role="switch"]').at(0);
|
||||
findByTestId(component, "enable-live-share-toggle").find('[role="switch"]').at(0);
|
||||
const getSubmitEnableButton = (component: ReactWrapper) =>
|
||||
findByTestId(component, 'enable-live-share-submit').at(0);
|
||||
findByTestId(component, "enable-live-share-submit").at(0);
|
||||
|
||||
it('goes to labs flag screen after live options is clicked', () => {
|
||||
it("goes to labs flag screen after live options is clicked", () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
|
||||
setShareType(component, LocationShareType.Live);
|
||||
|
||||
expect(findByTestId(component, 'location-picker-enable-live-share')).toMatchSnapshot();
|
||||
expect(findByTestId(component, "location-picker-enable-live-share")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('disables OK button when labs flag is not enabled', () => {
|
||||
it("disables OK button when labs flag is not enabled", () => {
|
||||
const component = getComponent();
|
||||
|
||||
setShareType(component, LocationShareType.Live);
|
||||
|
||||
expect(getSubmitEnableButton(component).props()['disabled']).toBeTruthy();
|
||||
expect(getSubmitEnableButton(component).props()["disabled"]).toBeTruthy();
|
||||
});
|
||||
|
||||
it('enables OK button when labs flag is toggled to enabled', () => {
|
||||
it("enables OK button when labs flag is toggled to enabled", () => {
|
||||
const component = getComponent();
|
||||
|
||||
setShareType(component, LocationShareType.Live);
|
||||
|
||||
act(() => {
|
||||
getToggle(component).simulate('click');
|
||||
getToggle(component).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
expect(getSubmitEnableButton(component).props()['disabled']).toBeFalsy();
|
||||
expect(getSubmitEnableButton(component).props()["disabled"]).toBeFalsy();
|
||||
});
|
||||
|
||||
it('enables live share setting on ok button submit', () => {
|
||||
it("enables live share setting on ok button submit", () => {
|
||||
const component = getComponent();
|
||||
|
||||
setShareType(component, LocationShareType.Live);
|
||||
|
||||
act(() => {
|
||||
getToggle(component).simulate('click');
|
||||
getToggle(component).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
act(() => {
|
||||
getSubmitEnableButton(component).simulate('click');
|
||||
getSubmitEnableButton(component).simulate("click");
|
||||
});
|
||||
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith(
|
||||
'feature_location_share_live', undefined, SettingLevel.DEVICE, true,
|
||||
"feature_location_share_live",
|
||||
undefined,
|
||||
SettingLevel.DEVICE,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates to location picker when live share is enabled in settings store', () => {
|
||||
it("navigates to location picker when live share is enabled in settings store", () => {
|
||||
// @ts-ignore
|
||||
mocked(SettingsStore.watchSetting).mockImplementation((featureName, roomId, callback) => {
|
||||
callback(featureName, roomId, SettingLevel.DEVICE, '', '');
|
||||
callback(featureName, roomId, SettingLevel.DEVICE, "", "");
|
||||
window.setTimeout(() => {
|
||||
callback(featureName, roomId, SettingLevel.DEVICE, '', '');
|
||||
callback(featureName, roomId, SettingLevel.DEVICE, "", "");
|
||||
}, 1000);
|
||||
});
|
||||
mocked(SettingsStore.getValue).mockReturnValue(false);
|
||||
@@ -362,7 +370,7 @@ describe('<LocationShareMenu />', () => {
|
||||
setShareType(component, LocationShareType.Live);
|
||||
|
||||
// we're on enable live share screen
|
||||
expect(findByTestId(component, 'location-picker-enable-live-share').length).toBeTruthy();
|
||||
expect(findByTestId(component, "location-picker-enable-live-share").length).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
mocked(SettingsStore.getValue).mockReturnValue(true);
|
||||
@@ -373,24 +381,24 @@ describe('<LocationShareMenu />', () => {
|
||||
component.setProps({});
|
||||
|
||||
// advanced to location picker
|
||||
expect(component.find('LocationPicker').length).toBeTruthy();
|
||||
expect(component.find("LocationPicker").length).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Live location share', () => {
|
||||
describe("Live location share", () => {
|
||||
beforeEach(() => enableSettings(["feature_location_share_live"]));
|
||||
|
||||
it('does not display live location share option when composer has a relation', () => {
|
||||
it("does not display live location share option when composer has a relation", () => {
|
||||
const relation = {
|
||||
rel_type: RelationType.Thread,
|
||||
event_id: '12345',
|
||||
event_id: "12345",
|
||||
};
|
||||
const component = getComponent({ relation });
|
||||
|
||||
expect(getShareTypeOption(component, LocationShareType.Live).length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('creates beacon info event on submission', async () => {
|
||||
it("creates beacon info event on submission", async () => {
|
||||
const onFinished = jest.fn();
|
||||
const component = getComponent({ onFinished });
|
||||
|
||||
@@ -399,7 +407,7 @@ describe('<LocationShareMenu />', () => {
|
||||
setLocation(component);
|
||||
|
||||
act(() => {
|
||||
getSubmitButton(component).at(0).simulate('click');
|
||||
getSubmitButton(component).at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
@@ -409,21 +417,23 @@ describe('<LocationShareMenu />', () => {
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
const [eventRoomId, eventContent] = mockClient.unstable_createLiveBeacon.mock.calls[0];
|
||||
expect(eventRoomId).toEqual(defaultProps.roomId);
|
||||
expect(eventContent).toEqual(expect.objectContaining({
|
||||
// default timeout
|
||||
timeout: DEFAULT_DURATION_MS,
|
||||
description: `Ernie's live location`,
|
||||
live: true,
|
||||
[M_ASSET.name]: {
|
||||
type: LocationAssetType.Self,
|
||||
},
|
||||
}));
|
||||
expect(eventContent).toEqual(
|
||||
expect.objectContaining({
|
||||
// default timeout
|
||||
timeout: DEFAULT_DURATION_MS,
|
||||
description: `Ernie's live location`,
|
||||
live: true,
|
||||
[M_ASSET.name]: {
|
||||
type: LocationAssetType.Self,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('opens error dialog when beacon creation fails', async () => {
|
||||
it("opens error dialog when beacon creation fails", async () => {
|
||||
// stub logger to keep console clean from expected error
|
||||
const logSpy = jest.spyOn(logger, 'error').mockReturnValue(undefined);
|
||||
const error = new Error('oh no');
|
||||
const logSpy = jest.spyOn(logger, "error").mockReturnValue(undefined);
|
||||
const error = new Error("oh no");
|
||||
mockClient.unstable_createLiveBeacon.mockRejectedValue(error);
|
||||
const component = getComponent();
|
||||
|
||||
@@ -432,7 +442,7 @@ describe('<LocationShareMenu />', () => {
|
||||
setLocation(component);
|
||||
|
||||
act(() => {
|
||||
getSubmitButton(component).at(0).simulate('click');
|
||||
getSubmitButton(component).at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
@@ -441,18 +451,21 @@ describe('<LocationShareMenu />', () => {
|
||||
await flushPromisesWithFakeTimers();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith("We couldn't start sharing your live location", error);
|
||||
expect(mocked(Modal).createDialog).toHaveBeenCalledWith(QuestionDialog, expect.objectContaining({
|
||||
button: 'Try again',
|
||||
description: 'Element could not send your location. Please try again later.',
|
||||
title: `We couldn't send your location`,
|
||||
cancelButton: 'Cancel',
|
||||
}));
|
||||
expect(mocked(Modal).createDialog).toHaveBeenCalledWith(
|
||||
QuestionDialog,
|
||||
expect.objectContaining({
|
||||
button: "Try again",
|
||||
description: "Element could not send your location. Please try again later.",
|
||||
title: `We couldn't send your location`,
|
||||
cancelButton: "Cancel",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('opens error dialog when beacon creation fails with permission error', async () => {
|
||||
it("opens error dialog when beacon creation fails with permission error", async () => {
|
||||
// stub logger to keep console clean from expected error
|
||||
const logSpy = jest.spyOn(logger, 'error').mockReturnValue(undefined);
|
||||
const error = { errcode: 'M_FORBIDDEN' } as unknown as Error;
|
||||
const logSpy = jest.spyOn(logger, "error").mockReturnValue(undefined);
|
||||
const error = { errcode: "M_FORBIDDEN" } as unknown as Error;
|
||||
mockClient.unstable_createLiveBeacon.mockRejectedValue(error);
|
||||
const component = getComponent();
|
||||
|
||||
@@ -461,7 +474,7 @@ describe('<LocationShareMenu />', () => {
|
||||
setLocation(component);
|
||||
|
||||
act(() => {
|
||||
getSubmitButton(component).at(0).simulate('click');
|
||||
getSubmitButton(component).at(0).simulate("click");
|
||||
component.setProps({});
|
||||
});
|
||||
|
||||
@@ -470,12 +483,15 @@ describe('<LocationShareMenu />', () => {
|
||||
await flushPromisesWithFakeTimers();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith("Insufficient permissions to start sharing your live location", error);
|
||||
expect(mocked(Modal).createDialog).toHaveBeenCalledWith(QuestionDialog, expect.objectContaining({
|
||||
button: 'OK',
|
||||
description: 'You need to have the right permissions in order to share locations in this room.',
|
||||
title: `You don't have permission to share locations`,
|
||||
hasCancelButton: false,
|
||||
}));
|
||||
expect(mocked(Modal).createDialog).toHaveBeenCalledWith(
|
||||
QuestionDialog,
|
||||
expect.objectContaining({
|
||||
button: "OK",
|
||||
description: "You need to have the right permissions in order to share locations in this room.",
|
||||
title: `You don't have permission to share locations`,
|
||||
hasCancelButton: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,23 +14,23 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { RoomMember } from 'matrix-js-sdk/src/matrix';
|
||||
import { LocationAssetType } from 'matrix-js-sdk/src/@types/location';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { mount } from "enzyme";
|
||||
import { RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
import { LocationAssetType } from "matrix-js-sdk/src/@types/location";
|
||||
import maplibregl from "maplibre-gl";
|
||||
|
||||
import LocationViewDialog from '../../../../src/components/views/location/LocationViewDialog';
|
||||
import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils';
|
||||
import { getMockClientWithEventEmitter, makeLocationEvent } from '../../../test-utils';
|
||||
import LocationViewDialog from "../../../../src/components/views/location/LocationViewDialog";
|
||||
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
||||
import { getMockClientWithEventEmitter, makeLocationEvent } from "../../../test-utils";
|
||||
|
||||
describe('<LocationViewDialog />', () => {
|
||||
const roomId = '!room:server';
|
||||
const userId = '@user:server';
|
||||
describe("<LocationViewDialog />", () => {
|
||||
const roomId = "!room:server";
|
||||
const userId = "@user:server";
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getClientWellKnown: jest.fn().mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
}),
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
});
|
||||
@@ -40,24 +40,23 @@ describe('<LocationViewDialog />', () => {
|
||||
mxEvent: defaultEvent,
|
||||
onFinished: jest.fn(),
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<LocationViewDialog {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => mount(<LocationViewDialog {...defaultProps} {...props} />);
|
||||
|
||||
beforeAll(() => {
|
||||
maplibregl.AttributionControl = jest.fn();
|
||||
});
|
||||
|
||||
it('renders map correctly', () => {
|
||||
it("renders map correctly", () => {
|
||||
const component = getComponent();
|
||||
expect(component.find('Map')).toMatchSnapshot();
|
||||
expect(component.find("Map")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders marker correctly for self share', () => {
|
||||
it("renders marker correctly for self share", () => {
|
||||
const selfShareEvent = makeLocationEvent("geo:51.5076,-0.1276", LocationAssetType.Self);
|
||||
const member = new RoomMember(roomId, userId);
|
||||
// @ts-ignore cheat assignment to property
|
||||
selfShareEvent.sender = member;
|
||||
const component = getComponent({ mxEvent: selfShareEvent });
|
||||
expect(component.find('SmartMarker').props()['roomMember']).toEqual(member);
|
||||
expect(component.find("SmartMarker").props()["roomMember"]).toEqual(member);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,29 +14,29 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { ClientEvent } from 'matrix-js-sdk/src/matrix';
|
||||
import { logger } from 'matrix-js-sdk/src/logger';
|
||||
import { mount } from "enzyme";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import maplibregl from "maplibre-gl";
|
||||
import { ClientEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import Map from '../../../../src/components/views/location/Map';
|
||||
import { findByTestId, getMockClientWithEventEmitter } from '../../../test-utils';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils';
|
||||
import Map from "../../../../src/components/views/location/Map";
|
||||
import { findByTestId, getMockClientWithEventEmitter } from "../../../test-utils";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
||||
|
||||
describe('<Map />', () => {
|
||||
describe("<Map />", () => {
|
||||
const defaultProps = {
|
||||
centerGeoUri: 'geo:52,41',
|
||||
id: 'test-123',
|
||||
centerGeoUri: "geo:52,41",
|
||||
id: "test-123",
|
||||
onError: jest.fn(),
|
||||
onClick: jest.fn(),
|
||||
};
|
||||
const matrixClient = getMockClientWithEventEmitter({
|
||||
getClientWellKnown: jest.fn().mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
}),
|
||||
});
|
||||
const getComponent = (props = {}) =>
|
||||
@@ -52,33 +52,33 @@ describe('<Map />', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
matrixClient.getClientWellKnown.mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
});
|
||||
|
||||
jest.spyOn(logger, 'error').mockRestore();
|
||||
jest.spyOn(logger, "error").mockRestore();
|
||||
});
|
||||
|
||||
const mockMap = new maplibregl.Map();
|
||||
|
||||
it('renders', () => {
|
||||
it("renders", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('onClientWellKnown emits', () => {
|
||||
it('updates map style when style url is truthy', () => {
|
||||
describe("onClientWellKnown emits", () => {
|
||||
it("updates map style when style url is truthy", () => {
|
||||
getComponent();
|
||||
|
||||
act(() => {
|
||||
matrixClient.emit(ClientEvent.ClientWellKnown, {
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'new.maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "new.maps.com" },
|
||||
});
|
||||
});
|
||||
|
||||
expect(mockMap.setStyle).toHaveBeenCalledWith('new.maps.com');
|
||||
expect(mockMap.setStyle).toHaveBeenCalledWith("new.maps.com");
|
||||
});
|
||||
|
||||
it('does not update map style when style url is truthy', () => {
|
||||
it("does not update map style when style url is truthy", () => {
|
||||
getComponent();
|
||||
|
||||
act(() => {
|
||||
@@ -91,58 +91,60 @@ describe('<Map />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('map centering', () => {
|
||||
it('does not try to center when no center uri provided', () => {
|
||||
describe("map centering", () => {
|
||||
it("does not try to center when no center uri provided", () => {
|
||||
getComponent({ centerGeoUri: null });
|
||||
expect(mockMap.setCenter).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('sets map center to centerGeoUri', () => {
|
||||
getComponent({ centerGeoUri: 'geo:51,42' });
|
||||
it("sets map center to centerGeoUri", () => {
|
||||
getComponent({ centerGeoUri: "geo:51,42" });
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 51, lon: 42 });
|
||||
});
|
||||
|
||||
it('handles invalid centerGeoUri', () => {
|
||||
const logSpy = jest.spyOn(logger, 'error').mockImplementation();
|
||||
getComponent({ centerGeoUri: '123 Sesame Street' });
|
||||
it("handles invalid centerGeoUri", () => {
|
||||
const logSpy = jest.spyOn(logger, "error").mockImplementation();
|
||||
getComponent({ centerGeoUri: "123 Sesame Street" });
|
||||
expect(mockMap.setCenter).not.toHaveBeenCalled();
|
||||
expect(logSpy).toHaveBeenCalledWith('Could not set map center');
|
||||
expect(logSpy).toHaveBeenCalledWith("Could not set map center");
|
||||
});
|
||||
|
||||
it('updates map center when centerGeoUri prop changes', () => {
|
||||
const component = getComponent({ centerGeoUri: 'geo:51,42' });
|
||||
it("updates map center when centerGeoUri prop changes", () => {
|
||||
const component = getComponent({ centerGeoUri: "geo:51,42" });
|
||||
|
||||
component.setProps({ centerGeoUri: 'geo:53,45' });
|
||||
component.setProps({ centerGeoUri: 'geo:56,47' });
|
||||
component.setProps({ centerGeoUri: "geo:53,45" });
|
||||
component.setProps({ centerGeoUri: "geo:56,47" });
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 51, lon: 42 });
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 53, lon: 45 });
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 56, lon: 47 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('map bounds', () => {
|
||||
it('does not try to fit map bounds when no bounds provided', () => {
|
||||
describe("map bounds", () => {
|
||||
it("does not try to fit map bounds when no bounds provided", () => {
|
||||
getComponent({ bounds: null });
|
||||
expect(mockMap.fitBounds).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fits map to bounds', () => {
|
||||
it("fits map to bounds", () => {
|
||||
const bounds = { north: 51, south: 50, east: 42, west: 41 };
|
||||
getComponent({ bounds });
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(new maplibregl.LngLatBounds([bounds.west, bounds.south],
|
||||
[bounds.east, bounds.north]), { padding: 100, maxZoom: 15 });
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(
|
||||
new maplibregl.LngLatBounds([bounds.west, bounds.south], [bounds.east, bounds.north]),
|
||||
{ padding: 100, maxZoom: 15 },
|
||||
);
|
||||
});
|
||||
|
||||
it('handles invalid bounds', () => {
|
||||
const logSpy = jest.spyOn(logger, 'error').mockImplementation();
|
||||
const bounds = { north: 'a', south: 'b', east: 42, west: 41 };
|
||||
it("handles invalid bounds", () => {
|
||||
const logSpy = jest.spyOn(logger, "error").mockImplementation();
|
||||
const bounds = { north: "a", south: "b", east: 42, west: 41 };
|
||||
getComponent({ bounds });
|
||||
expect(mockMap.fitBounds).not.toHaveBeenCalled();
|
||||
expect(logSpy).toHaveBeenCalledWith('Invalid map bounds');
|
||||
expect(logSpy).toHaveBeenCalledWith("Invalid map bounds");
|
||||
});
|
||||
|
||||
it('updates map bounds when bounds prop changes', () => {
|
||||
const component = getComponent({ centerGeoUri: 'geo:51,42' });
|
||||
it("updates map bounds when bounds prop changes", () => {
|
||||
const component = getComponent({ centerGeoUri: "geo:51,42" });
|
||||
|
||||
const bounds = { north: 51, south: 50, east: 42, west: 41 };
|
||||
const bounds2 = { north: 53, south: 51, east: 45, west: 44 };
|
||||
@@ -152,8 +154,8 @@ describe('<Map />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('children', () => {
|
||||
it('renders without children', () => {
|
||||
describe("children", () => {
|
||||
it("renders without children", () => {
|
||||
const component = getComponent({ children: null });
|
||||
|
||||
component.setProps({});
|
||||
@@ -162,18 +164,22 @@ describe('<Map />', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders children with map renderProp', () => {
|
||||
const children = ({ map }) => <div data-test-id='test-child' data-map={map}>Hello, world</div>;
|
||||
it("renders children with map renderProp", () => {
|
||||
const children = ({ map }) => (
|
||||
<div data-test-id="test-child" data-map={map}>
|
||||
Hello, world
|
||||
</div>
|
||||
);
|
||||
|
||||
const component = getComponent({ children });
|
||||
|
||||
// renders child with map instance
|
||||
expect(findByTestId(component, 'test-child').props()['data-map']).toEqual(mockMap);
|
||||
expect(findByTestId(component, "test-child").props()["data-map"]).toEqual(mockMap);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onClick', () => {
|
||||
it('eats clicks to maplibre attribution button', () => {
|
||||
describe("onClick", () => {
|
||||
it("eats clicks to maplibre attribution button", () => {
|
||||
const onClick = jest.fn();
|
||||
const component = getComponent({ onClick });
|
||||
|
||||
@@ -181,20 +187,20 @@ describe('<Map />', () => {
|
||||
// this is added to the dom by maplibregl
|
||||
// which is mocked
|
||||
// just fake the target
|
||||
const fakeEl = document.createElement('div');
|
||||
fakeEl.className = 'maplibregl-ctrl-attrib-button';
|
||||
component.simulate('click', { target: fakeEl });
|
||||
const fakeEl = document.createElement("div");
|
||||
fakeEl.className = "maplibregl-ctrl-attrib-button";
|
||||
component.simulate("click", { target: fakeEl });
|
||||
});
|
||||
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls onClick', () => {
|
||||
it("calls onClick", () => {
|
||||
const onClick = jest.fn();
|
||||
const component = getComponent({ onClick });
|
||||
|
||||
act(() => {
|
||||
component.simulate('click');
|
||||
component.simulate("click");
|
||||
});
|
||||
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
|
||||
@@ -14,43 +14,43 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, RenderResult } from '@testing-library/react';
|
||||
import React from "react";
|
||||
import { render, RenderResult } from "@testing-library/react";
|
||||
|
||||
import { MapError, MapErrorProps } from '../../../../src/components/views/location/MapError';
|
||||
import { LocationShareError } from '../../../../src/utils/location';
|
||||
import { MapError, MapErrorProps } from "../../../../src/components/views/location/MapError";
|
||||
import { LocationShareError } from "../../../../src/utils/location";
|
||||
|
||||
describe('<MapError />', () => {
|
||||
describe("<MapError />", () => {
|
||||
const defaultProps = {
|
||||
onFinished: jest.fn(),
|
||||
error: LocationShareError.MapStyleUrlNotConfigured,
|
||||
className: 'test',
|
||||
className: "test",
|
||||
};
|
||||
const getComponent = (props: Partial<MapErrorProps> = {}): RenderResult =>
|
||||
render(<MapError {...defaultProps} {...props} />);
|
||||
|
||||
it('renders correctly for MapStyleUrlNotConfigured', () => {
|
||||
it("renders correctly for MapStyleUrlNotConfigured", () => {
|
||||
const { container } = getComponent();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders correctly for MapStyleUrlNotReachable', () => {
|
||||
it("renders correctly for MapStyleUrlNotReachable", () => {
|
||||
const { container } = getComponent({
|
||||
error: LocationShareError.MapStyleUrlNotReachable,
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('does not render button when onFinished falsy', () => {
|
||||
it("does not render button when onFinished falsy", () => {
|
||||
const { queryByText } = getComponent({
|
||||
error: LocationShareError.MapStyleUrlNotReachable,
|
||||
onFinished: undefined,
|
||||
});
|
||||
// no button
|
||||
expect(queryByText('OK')).toBeFalsy();
|
||||
expect(queryByText("OK")).toBeFalsy();
|
||||
});
|
||||
|
||||
it('applies class when isMinimised is truthy', () => {
|
||||
it("applies class when isMinimised is truthy", () => {
|
||||
const { container } = getComponent({
|
||||
isMinimised: true,
|
||||
});
|
||||
|
||||
@@ -14,39 +14,38 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { RoomMember } from 'matrix-js-sdk/src/matrix';
|
||||
import { mount } from "enzyme";
|
||||
import { RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import Marker from '../../../../src/components/views/location/Marker';
|
||||
import Marker from "../../../../src/components/views/location/Marker";
|
||||
|
||||
describe('<Marker />', () => {
|
||||
describe("<Marker />", () => {
|
||||
const defaultProps = {
|
||||
id: 'abc123',
|
||||
id: "abc123",
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<Marker {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => mount(<Marker {...defaultProps} {...props} />);
|
||||
|
||||
it('renders with location icon when no room member', () => {
|
||||
it("renders with location icon when no room member", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('does not try to use member color without room member', () => {
|
||||
it("does not try to use member color without room member", () => {
|
||||
const component = getComponent({ useMemberColor: true });
|
||||
expect(component.find('div').at(0).props().className).toEqual('mx_Marker mx_Marker_defaultColor');
|
||||
expect(component.find("div").at(0).props().className).toEqual("mx_Marker mx_Marker_defaultColor");
|
||||
});
|
||||
|
||||
it('uses member color class', () => {
|
||||
const member = new RoomMember('!room:server', '@user:server');
|
||||
it("uses member color class", () => {
|
||||
const member = new RoomMember("!room:server", "@user:server");
|
||||
const component = getComponent({ useMemberColor: true, roomMember: member });
|
||||
expect(component.find('div').at(0).props().className).toEqual('mx_Marker mx_Username_color3');
|
||||
expect(component.find("div").at(0).props().className).toEqual("mx_Marker mx_Username_color3");
|
||||
});
|
||||
|
||||
it('renders member avatar when roomMember is truthy', () => {
|
||||
const member = new RoomMember('!room:server', '@user:server');
|
||||
it("renders member avatar when roomMember is truthy", () => {
|
||||
const member = new RoomMember("!room:server", "@user:server");
|
||||
const component = getComponent({ roomMember: member });
|
||||
expect(component.find('MemberAvatar').length).toBeTruthy();
|
||||
expect(component.find("MemberAvatar").length).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,34 +14,33 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { mocked } from 'jest-mock';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { mount } from "enzyme";
|
||||
import { mocked } from "jest-mock";
|
||||
import maplibregl from "maplibre-gl";
|
||||
|
||||
import SmartMarker from '../../../../src/components/views/location/SmartMarker';
|
||||
import SmartMarker from "../../../../src/components/views/location/SmartMarker";
|
||||
|
||||
jest.mock('../../../../src/utils/location/findMapStyleUrl', () => ({
|
||||
findMapStyleUrl: jest.fn().mockReturnValue('tileserver.com'),
|
||||
jest.mock("../../../../src/utils/location/findMapStyleUrl", () => ({
|
||||
findMapStyleUrl: jest.fn().mockReturnValue("tileserver.com"),
|
||||
}));
|
||||
|
||||
describe('<SmartMarker />', () => {
|
||||
describe("<SmartMarker />", () => {
|
||||
const mockMap = new maplibregl.Map();
|
||||
const mockMarker = new maplibregl.Marker();
|
||||
|
||||
const defaultProps = {
|
||||
map: mockMap,
|
||||
geoUri: 'geo:43.2,54.6',
|
||||
geoUri: "geo:43.2,54.6",
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<SmartMarker {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => mount(<SmartMarker {...defaultProps} {...props} />);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('creates a marker on mount', () => {
|
||||
it("creates a marker on mount", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toMatchSnapshot();
|
||||
|
||||
@@ -55,11 +54,11 @@ describe('<SmartMarker />', () => {
|
||||
expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap);
|
||||
});
|
||||
|
||||
it('updates marker position on change', () => {
|
||||
const component = getComponent({ geoUri: 'geo:40,50' });
|
||||
it("updates marker position on change", () => {
|
||||
const component = getComponent({ geoUri: "geo:40,50" });
|
||||
|
||||
component.setProps({ geoUri: 'geo:41,51' });
|
||||
component.setProps({ geoUri: 'geo:42,52' });
|
||||
component.setProps({ geoUri: "geo:41,51" });
|
||||
component.setProps({ geoUri: "geo:42,52" });
|
||||
|
||||
// marker added only once
|
||||
expect(maplibregl.Marker).toHaveBeenCalledTimes(1);
|
||||
@@ -69,7 +68,7 @@ describe('<SmartMarker />', () => {
|
||||
expect(mocked(mockMarker.setLngLat)).toHaveBeenCalledWith({ lat: 42, lon: 52 });
|
||||
});
|
||||
|
||||
it('removes marker on unmount', () => {
|
||||
it("removes marker on unmount", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toMatchSnapshot();
|
||||
|
||||
|
||||
@@ -14,48 +14,47 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mount } from "enzyme";
|
||||
import maplibregl from "maplibre-gl";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import ZoomButtons from '../../../../src/components/views/location/ZoomButtons';
|
||||
import { findByTestId } from '../../../test-utils';
|
||||
import ZoomButtons from "../../../../src/components/views/location/ZoomButtons";
|
||||
import { findByTestId } from "../../../test-utils";
|
||||
|
||||
describe('<ZoomButtons />', () => {
|
||||
describe("<ZoomButtons />", () => {
|
||||
const mockMap = new maplibregl.Map();
|
||||
const defaultProps = {
|
||||
map: mockMap,
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<ZoomButtons {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => mount(<ZoomButtons {...defaultProps} {...props} />);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders buttons', () => {
|
||||
it("renders buttons", () => {
|
||||
const component = getComponent();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('calls map zoom in on zoom in click', () => {
|
||||
it("calls map zoom in on zoom in click", () => {
|
||||
const component = getComponent();
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'map-zoom-in-button').at(0).simulate('click');
|
||||
findByTestId(component, "map-zoom-in-button").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(mockMap.zoomIn).toHaveBeenCalled();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('calls map zoom out on zoom out click', () => {
|
||||
it("calls map zoom out on zoom out click", () => {
|
||||
const component = getComponent();
|
||||
|
||||
act(() => {
|
||||
findByTestId(component, 'map-zoom-out-button').at(0).simulate('click');
|
||||
findByTestId(component, "map-zoom-out-button").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(mockMap.zoomOut).toHaveBeenCalled();
|
||||
|
||||
@@ -47,13 +47,11 @@ describe("shareLocation", () => {
|
||||
} as unknown as MatrixClient;
|
||||
|
||||
mocked(makeLocationContent).mockReturnValue(content);
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(<T>(
|
||||
roomId: string,
|
||||
fn: (actualRoomId: string) => Promise<T>,
|
||||
client?: MatrixClient,
|
||||
) => {
|
||||
return fn(roomId);
|
||||
});
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(
|
||||
<T>(roomId: string, fn: (actualRoomId: string) => Promise<T>, client?: MatrixClient) => {
|
||||
return fn(roomId);
|
||||
},
|
||||
);
|
||||
|
||||
shareLocationFn = shareLocation(client, roomId, shareType, null, () => {});
|
||||
});
|
||||
|
||||
@@ -68,16 +68,18 @@ describe("CallEvent", () => {
|
||||
alice = mkRoomMember(room.roomId, "@alice:example.org");
|
||||
bob = mkRoomMember(room.roomId, "@bob:example.org");
|
||||
jest.spyOn(room, "getMember").mockImplementation(
|
||||
userId => [alice, bob].find(member => member.userId === userId) ?? null,
|
||||
(userId) => [alice, bob].find((member) => member.userId === userId) ?? null,
|
||||
);
|
||||
|
||||
client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null);
|
||||
client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
|
||||
client.getRooms.mockReturnValue([room]);
|
||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||
|
||||
await Promise.all([CallStore.instance, WidgetMessagingStore.instance].map(
|
||||
store => setupAsyncStoreWithClient(store, client),
|
||||
));
|
||||
await Promise.all(
|
||||
[CallStore.instance, WidgetMessagingStore.instance].map((store) =>
|
||||
setupAsyncStoreWithClient(store, client),
|
||||
),
|
||||
);
|
||||
|
||||
MockedCall.create(room, "1");
|
||||
const maybeCall = CallStore.instance.getCall(room.roomId);
|
||||
@@ -99,7 +101,9 @@ describe("CallEvent", () => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const renderEvent = () => { render(<CallEvent mxEvent={call.event} />); };
|
||||
const renderEvent = () => {
|
||||
render(<CallEvent mxEvent={call.event} />);
|
||||
};
|
||||
|
||||
it("shows a message and duration if the call was ended", () => {
|
||||
jest.advanceTimersByTime(90000);
|
||||
@@ -121,7 +125,10 @@ describe("CallEvent", () => {
|
||||
|
||||
it("shows call details and connection controls if the call is loaded", async () => {
|
||||
jest.advanceTimersByTime(90000);
|
||||
call.participants = new Map([[alice, new Set(["a"])], [bob, new Set(["b"])]]);
|
||||
call.participants = new Map([
|
||||
[alice, new Set(["a"])],
|
||||
[bob, new Set(["b"])],
|
||||
]);
|
||||
renderEvent();
|
||||
|
||||
screen.getByText("@alice:example.org started a video call");
|
||||
@@ -132,11 +139,13 @@ describe("CallEvent", () => {
|
||||
const dispatcherSpy = jest.fn();
|
||||
const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
|
||||
fireEvent.click(screen.getByRole("button", { name: "Join" }));
|
||||
await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
view_call: true,
|
||||
}));
|
||||
await waitFor(() =>
|
||||
expect(dispatcherSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
view_call: true,
|
||||
}),
|
||||
);
|
||||
defaultDispatcher.unregister(dispatcherRef);
|
||||
await act(() => call.connect());
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user